1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2009 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 A Signer backed by a X.509 certificate 25 */ 26 27 Holder = require('scsh/pki-db/Holder').Holder; 28 Certificate = require('scsh/pki-db/Certificate').Certificate; 29 PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon; 30 PKCS10 = require('scsh/pkcs/PKCS10').PKCS10; 31 PKCS10Generator = require('scsh/x509/PKCS10Generator').PKCS10Generator; 32 LongDate = require('scsh/pki-db/LongDate').LongDate; 33 34 35 36 /** 37 * Create a signer based on a X.509 certificate 38 * 39 * @class Class implementing a signer backed by a X.509 certificate 40 * @constructor 41 * @param {DAOFactory} daof the factory that can create the required data access objects 42 * @param {CryptoProviderFactory} cpf factory implementing getCryptoProvider() used to get access to crypto providers 43 * @param {Holder} holder the holder object for this signer 44 */ 45 function X509Signer(daof, cpf, holder) { 46 this.daof = daof; 47 this.cpf = cpf; 48 this.holder = holder; 49 50 if (this.holder == null) { 51 throw new GPError(module.id, GPError.INVALID_DATA, 1, "Certificate holder " + holder + " not found"); 52 } 53 54 if (this.getSigner()) { 55 this.parsePolicyFromSigner(); 56 } 57 } 58 59 exports.X509Signer = X509Signer; 60 61 62 63 /** 64 * Create a new signer 65 * 66 * @param {DAOFactory} daof the factory that can create the required data access objects 67 * @param {String/Number} pathOrHolderId the path of holderIDs (eg. "/UTCVCA/UTDVCA/UTTERM") or the holderId from the database 68 * @param {Number} certtype optional argument, default Holder.X509 69 * @type Number 70 * @return the newly created holder id 71 */ 72 X509Signer.createSigner = function(daof, pathOrHolderId, certtype) { 73 if (typeof(certtype) == "undefined") { 74 certtype = Holder.X509; 75 } 76 77 var holderdao = daof.getHolderDAO(); 78 if (typeof(pathOrHolderId) == "string") { 79 var holder = holderdao.newHolder(pathOrHolderId, certtype); 80 } else { 81 if (typeof(pathOrHolderId) == "undefined") { 82 pathOrHolderId = 0; 83 } 84 85 var holder = holderdao.newHolderForParent(pathOrHolderId, certtype); 86 } 87 88 return holder.id; 89 } 90 91 92 93 /** 94 * Set policy for signer object. 95 * 96 * The policy object shall contain the following properties 97 * 98 * <ul> 99 * <li>distinguishedName - The distinguishedName object as defined in PKIXCommon.encodeName()</li> 100 * <li>keySpecification - A Key object initialized with the key parameter.</li> 101 * <li>signatureAlgorithm - A ByteString encoding the object identifier for the signature algorithm</li> 102 * <li>validityDaysSelfSigned - Number of days the self-signed certificate is valid</li> 103 * <li>validityDays - Number of days the issued certificate is valid</li> 104 * <li>pathLenConstraint - Number of subordinate CAs</li> 105 * <li>requestFormat - "pkcs10" or "sc-hsm"</li> 106 * <li>overwriteKey - Set to true to overwrite a key with the same label</li> 107 * </ul> 108 * 109 * @see PKIXCommon.encodeName() 110 * @param {String} crldp the URL of the distribution point 111 */ 112 X509Signer.prototype.setPolicy = function(policy) { 113 this.policy = policy; 114 } 115 116 117 118 /** 119 * Determine signer name for newly generated signer 120 * 121 * @type String 122 * @return the unique name 123 */ 124 X509Signer.prototype.determineSignerName = function() { 125 var name = this.holder.id + ":X509Signer " + this.holder.signerNo + 1 + " [" + Date() + "]"; 126 var holderdao = this.daof.getHolderDAO(); 127 holderdao.updateSignerNo(this.holder, this.holder.signerNo + 1); 128 return name; 129 } 130 131 132 133 /** 134 * Determine subject distinguished name for new signer 135 * 136 * @param {String} name 137 * @type String 138 * @return the distringuished name for the new signer 139 */ 140 X509Signer.prototype.determineDistinguishedName = function(name) { 141 return this.policy.distinguishedName; 142 } 143 144 145 146 /** 147 * Determine the key usage for the request 148 * 149 * @type Number 150 * @return the key usage defined in PKIXCommon 151 */ 152 X509Signer.prototype.getRequestKeyUsage = function() { 153 return PKIXCommon.digitalSignature; 154 } 155 156 157 158 /** 159 * Create a new signer key pair 160 * 161 * @param {String} name the signer name 162 * @type ByteString 163 * @return the subject key identifier 164 */ 165 X509Signer.prototype.newSigner = function(name, template) { 166 if (!name) { 167 name = this.determineSignerName(); 168 } 169 170 if (!template || !template.keyDomain) { 171 throw new GPError(module.id, GPError.INVALID_DATA, 1, "Cannot create a signer without a template containing the keyDomain"); 172 } 173 174 var cp = this.cpf.getCryptoProvider(template.keyDomain, true); 175 try { 176 if (this.policy.overwriteKey) { 177 cp.deleteIfExists = true; 178 } 179 180 var k = cp.generateKeyPair(name, this.policy.keySpecification); 181 var prk = k.prk; 182 var puk = k.puk; 183 var req = k.req; 184 var keyblob; 185 186 var keyId = PKIXCommon.determineKeyIdentifier(puk); 187 188 if (typeof(cp.getWrappedKey) == "function") { 189 template.keyblob = cp.getWrappedKey(name); 190 } 191 192 var signerDAO = this.daof.getSignerDAO(); 193 this.signer = signerDAO.newSigner(this.holder, name, keyId, template); 194 195 if (this.policy.requestFormat != "sc-hsm") { 196 var gen = new PKCS10Generator(cp.getCrypto()); 197 gen.setSignatureAlgorithm(this.policy.reqSignatureAlgorithm); 198 gen.setSubject(this.determineDistinguishedName()); 199 gen.setPublicKey(puk); 200 gen.addKeyUsageExtension(this.getRequestKeyUsage()); 201 req = gen.generateCertificationRequest(prk); 202 req = req.getBytes(); 203 } 204 205 var requestDAO = this.daof.getRequestDAO(); 206 requestDAO.newRequest(this.holder, keyId, req); 207 } 208 finally { 209 cp.release(); 210 } 211 212 return keyId; 213 } 214 215 216 217 /** 218 * Return the holderId from the holder database for this element 219 * 220 * @type Number 221 * @return the holderId 222 */ 223 X509Signer.prototype.getHolderId = function() { 224 return this.holder.id; 225 } 226 227 228 229 /** 230 * Return the holder from the holder database for this element 231 * 232 * @type Number 233 * @return the holderId 234 */ 235 X509Signer.prototype.getHolder = function() { 236 return this.holder; 237 } 238 239 240 241 /** 242 * Return the signer from the signer database for this element 243 * 244 * @type Signer 245 * @return the signer value object or null 246 */ 247 X509Signer.prototype.getSigner = function() { 248 GPSystem.log(GPSystem.DEBUG, module.id, "getSigner()"); 249 if (this.signer) { 250 GPSystem.log(GPSystem.DEBUG, module.id, "return cached signer"); 251 return this.signer; 252 } 253 254 var certDAO = this.daof.getCertificateDAO(); 255 this.currentCertificate = certDAO.getCurrentCertificate(this.holder); 256 257 if (!this.currentCertificate) { // no active signer 258 GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id); 259 return null; 260 } 261 262 var signerDAO = this.daof.getSignerDAO(); 263 this.signer = signerDAO.getSignerByKeyId(this.holder, this.currentCertificate.keyId); 264 265 if (this.signer) { 266 this.cpid = this.signer.keyDomain; 267 } 268 269 return this.signer; 270 } 271 272 273 274 /** 275 * Parse the policy from the signer's values object 276 * 277 * @type Object 278 * @return the policy 279 */ 280 X509Signer.prototype.parsePolicyFromSigner = function() { 281 var signer = this.getSigner(); 282 283 if (signer == null) { 284 throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer"); 285 } 286 287 var c = signer.getContent(); 288 289 if (!c || !c.keySpecification) { 290 return; // no policy 291 } 292 293 var dp = new Key(); 294 if (c.keySpecification.type == "EC") { 295 dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID)); 296 } else { 297 dp.setSize(c.keySpecification.keysize); 298 } 299 300 var dn = PKIXCommon.parseDN(c.distinguishedName); 301 302 this.policy = { 303 distinguishedName: dn, 304 signatureAlgorithm: c.signatureAlgorithm, 305 reqSignatureAlgorithm: c.keySpecification.sigalg, 306 validityDaysCertificates: c.validityDaysCertificates, 307 validityDaysCRL: c.validityDaysCRL, 308 pathLenConstraint: c.pathLenConstraint, 309 keySpecification: dp 310 } 311 312 return this.policy; 313 } 314 315 316 317 X509Signer.prototype.getSubjectPolicyForRequest = function() { 318 GPSystem.log(GPSystem.DEBUG, module.id, "getSubjectPolicyForRequest()"); 319 320 var signer = this.getSigner(); 321 322 if (signer == null) { 323 throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer"); 324 } 325 326 var c = signer.getContent(); 327 328 var dp = new Key(); 329 if (c.keySpecification.type == "EC") { 330 dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID)); 331 } else { 332 dp.setSize(c.keySpecification.keysize); 333 } 334 335 var policy = { 336 reqSignatureAlgorithm: c.keySpecification.sigalg, 337 keySpecification: dp 338 } 339 340 return policy; 341 } 342 343 344 345 /** 346 * Get request for the given subject key identifier 347 * 348 * @param {ByteString} keyId the subject key identifier 349 * @type ByteString 350 * @return the raw request 351 */ 352 X509Signer.prototype.getRequestBinary = function(keyId) { 353 var requestDAO = this.daof.getRequestDAO(); 354 var request = requestDAO.getRequestByKeyId(this.holder, keyId); 355 356 if (request == null) { 357 return null; 358 } 359 360 return request.bytes; 361 } 362 363 364 365 /** 366 * Get request for the given subject key identifier 367 * 368 * @param {ByteString} keyId the subject key identifier 369 * @type PKCS10 370 * @return the PKCS10 request 371 */ 372 X509Signer.prototype.getRequest = function(keyId) { 373 var requestDAO = this.daof.getRequestDAO(); 374 var request = requestDAO.getRequestByKeyId(this.holder, keyId); 375 376 if (request == null) { 377 return null; 378 } 379 380 var req = new PKCS10(request.bytes); 381 return req; 382 } 383 384 385 386 /* 387 X509Signer.prototype.activateSigner = function(name) { 388 } 389 390 391 392 X509Signer.prototype.importCertificate = function(name) { 393 } 394 395 396 397 X509Signer.prototype.getSignerList = function(usableOnly) { 398 } 399 */ 400 401 402 403 /** 404 * Store a certificate issued for a certain holder 405 * 406 * @param {X509} cert the certificate 407 * @param {Boolean} makeCurrent true if this certificate becomes the current certificate 408 * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier) 409 * @param {Number} srId service request id 410 * @type Number 411 * @return the database id of the certificate 412 */ 413 X509Signer.prototype.storeCertificateForHolder = function(holder, cert, makeCurrent, keyId, srId) { 414 GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificateForHolder(" + holder + "'" + cert + "'," + makeCurrent + "," + keyId + ")"); 415 416 var certdao = this.daof.getCertificateDAO(); 417 418 var issuer = cert.getIssuerDNString(); 419 var subject = cert.getSubjectDNString(); 420 var autid = cert.getAuthorityKeyIdentifier(); 421 var subid = cert.getSubjectKeyIdentifier(); 422 var serial = cert.getSerialNumber().toString(HEX); 423 424 if ((autid && autid.equals(subid)) || issuer.equals(subject)) { 425 var dir = Certificate.SHORT; 426 } else { 427 var dir = Certificate.UP; 428 } 429 430 var certificate = certdao.getCertificateBySerial(holder, serial, dir); 431 432 if (certificate) { 433 GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a different certificate for that serial number"); 434 GPSystem.log(GPSystem.INFO, module.id, "Existing: " + (new X509(certificate.bytes))); 435 GPSystem.log(GPSystem.INFO, module.id, "New : " + cert); 436 437 return; 438 } 439 440 if (typeof(keyId) == "undefined") { 441 keyId = PKIXCommon.determineKeyIdentifier(cert.getPublicKey()); 442 } 443 444 var template = { 445 keyId: keyId, 446 expiry: new LongDate(cert.getNotAfter()) 447 }; 448 449 if (srId) { 450 template.serviceRequestId = srId; 451 } 452 453 var certificate = certdao.newCertificate(holder, serial, dir, cert.getBytes(), template); 454 455 if (makeCurrent) { 456 var holderdao = this.daof.getHolderDAO(); 457 holderdao.updateCurrentCertificate(holder, certificate); 458 } 459 460 return certificate.id; 461 } 462 463 464 465 /** 466 * Store a certificate issued for this signer 467 * 468 * @param {X509} cert the certificate 469 * @param {Boolean} makeCurrent true if this certificate becomes the current certificate 470 * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier) 471 * @param {Number} srId service request id 472 */ 473 X509Signer.prototype.storeCertificate = function(cert, makeCurrent, keyId, srId) { 474 return this.storeCertificateForHolder(this.holder, cert, makeCurrent, keyId, srId); 475 } 476 477 478 479 /** 480 * Return the signer's certificate 481 * 482 * @type X509 483 * @return the signer's certificate 484 */ 485 X509Signer.prototype.getSignerCertificate = function() { 486 GPSystem.log(GPSystem.DEBUG, module.id, "getSignerCertificate()"); 487 if (!this.currentCertificate) { 488 this.getSigner(); 489 } 490 491 if (this.signer && !this.currentCertificate) { 492 var certDAO = this.daof.getCertificateDAO(); 493 this.currentCertificate = certDAO.getCurrentCertificate(this.holder); 494 } else if (!this.signer) { 495 GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id); 496 return null; 497 } 498 499 return new X509(this.currentCertificate.bytes); 500 } 501