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 card verifiable certificate store using a PKCS#11 device as key store 25 */ 26 27 // Imports 28 29 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference; 30 var CVC = require('scsh/eac/CVC').CVC; 31 var CVCertificateStore = require('scsh/eac/CVCertificateStore').CVCertificateStore; 32 var CVCCA = require('scsh/eac/CVCCA').CVCCA; 33 34 35 36 /** 37 * Create a CV certificate store using a PKCS#11 device as secure key store 38 * 39 * @class CV certificate store with PKCS#11 as secure key store 40 * @constructor 41 * @param {DAOFactory} daof the factory that can create data access objects for persistent information 42 * @param {PKCS11Session} p11session logged in PKCS#11 session with device 43 */ 44 function P11CVCertificateStore(daof, p11session) { 45 CVCertificateStore.call(this, daof); 46 this.p11session = p11session; 47 this.crypto = new P11Crypto(p11session); 48 } 49 50 P11CVCertificateStore.prototype = Object.create(CVCertificateStore.prototype); 51 P11CVCertificateStore.constructor = P11CVCertificateStore; 52 53 exports.P11CVCertificateStore = P11CVCertificateStore; 54 55 56 57 /** 58 * Get crypto object 59 * 60 * @type HSMCrypto 61 * @return the HSMCrypto object 62 */ 63 P11CVCertificateStore.prototype.getCrypto = function() { 64 return this.crypto; 65 } 66 67 68 69 /** 70 * Transform path and certificate holder into a label 71 * 72 * @param {String} path the path 73 * @param {PublicKeyReference} chr the certificate holder reference 74 * @type String 75 * @return the key label 76 */ 77 P11CVCertificateStore.path2label = function(path, chr) { 78 return path.substr(1) + chr.getSequenceNo(); 79 } 80 81 82 83 /** 84 * Get a private key in the certificate store. Overrides method in CVCertificateStore. 85 * 86 * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM") 87 * @param {PublicKeyReference} chr the public key reference for this key 88 * @returns the private key or null if not found 89 * @type Key 90 */ 91 P11CVCertificateStore.prototype.getPrivateKey = function(path, car) { 92 var label = P11CVCertificateStore.path2label(path, car); 93 94 var attr = new Array(); 95 attr[PKCS11Object.CKA_CLASS] = PKCS11Object.CKO_PRIVATE_KEY; 96 attr[PKCS11Object.CKA_LABEL] = new ByteString(label, ASCII); 97 var o = this.p11session.enumerateObjects(attr); 98 99 print(o.length); 100 for (var i = 0; i < o.length; i++) { 101 print(o[i].getAttribute(PKCS11Object.CKA_LABEL)); 102 } 103 if (o.length == 0) { 104 return null; 105 } 106 107 if (o.length != 1) { 108 throw new GPError("P11CVCertificateStore", GPError.INVALID_ARGUMENT, o.length, "Duplicate key with label " + label + " found"); 109 } 110 111 var p11key = o[0]; 112 113 var key = new Key(); 114 key.setType(Key.PRIVATE); 115 116 var curve = p11key.getAttribute(PKCS11Object.CKA_EC_PARAMS); 117 if (curve) { 118 var a = new ASN1(curve); 119 120 key.setComponent(Key.ECC_CURVE_OID, a.value); 121 } 122 key.p11 = p11key; 123 return key; 124 } 125 126 127 128 /** 129 * Determine curve from key parameter 130 * 131 * @param {Key} key the key 132 * @type ByteString 133 * @return the curve OID 134 */ 135 P11CVCertificateStore.determineCurve = function(key) { 136 var curves = [ 137 new ByteString("brainpoolP192r1", OID), 138 new ByteString("brainpoolP224r1", OID), 139 new ByteString("brainpoolP256r1", OID), 140 new ByteString("brainpoolP384r1", OID), 141 new ByteString("brainpoolP512r1", OID) 142 ]; 143 144 if (key.getComponent(Key.ECC_CURVE_OID)) { 145 return key.getComponent(Key.ECC_CURVE_OID); 146 } 147 148 for (var i = 0; i < curves.length; i++) { 149 var spec = new Key(); 150 spec.setComponent(Key.ECC_CURVE_OID, curves[i]); 151 if (key.getComponent(Key.ECC_P).equals(spec.getComponent(Key.ECC_P))) { 152 return curves[i]; 153 } 154 } 155 throw new GPError("P11CVCertificateStore", GPError.INVALID_ARGUMENT, 0, "Unknown curve"); 156 } 157 158 159 160 /** 161 * Generate key pair 162 * 163 * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM") 164 * @param {PublicKeyReference} chr the public key reference for this key pair 165 * @param {Number} algo the key generation algorithm (Crypto.EC or Crypto.RSA) 166 * @param {Key} prk the private key template 167 * @param {Key} puk the public key template 168 */ 169 P11CVCertificateStore.prototype.generateKeyPair = function(path, chr, algo, prk, puk) { 170 var label = P11CVCertificateStore.path2label(path, chr); 171 172 label = new ByteString(label, ASCII); 173 174 var priAttr = new Array(); 175 priAttr[PKCS11Object.CKA_TOKEN] = true; 176 priAttr[PKCS11Object.CKA_SIGN] = true; 177 priAttr[PKCS11Object.CKA_SENSITIVE] = true; 178 priAttr[PKCS11Object.CKA_PRIVATE] = true; 179 priAttr[PKCS11Object.CKA_LABEL] = label; 180 181 var pubAttr = new Array(); 182 pubAttr[PKCS11Object.CKA_TOKEN] = true; 183 pubAttr[PKCS11Object.CKA_VERIFY] = true; 184 pubAttr[PKCS11Object.CKA_LABEL] = label; 185 186 if (algo == Crypto.RSA) { 187 pubAttr[PKCS11Object.CKA_MODULUS_BITS] = puk.getSize(); 188 pubAttr[PKCS11Object.CKA_PUBLIC_EXPONENT] = new ByteString("010001", HEX); 189 var keys = this.p11session.generateKeyPair(PKCS11Session.CKM_RSA_PKCS_KEY_PAIR_GEN, null, pubAttr, priAttr); 190 191 var value = keys[0].getAttribute(PKCS11Object.CKA_VALUE); 192 var pk = new ASN1(value); 193 print(pk); 194 puk.setComponent(Key.MODULUS, pk.get(0).value.right(puk.getSize() >> 3)); 195 puk.setComponent(Key.EXPONENT, pk.get(1).value); 196 } else { 197 var curve = P11CVCertificateStore.determineCurve(puk); 198 var curveasn = new ASN1(ASN1.OBJECT_IDENTIFIER, curve); 199 200 pubAttr[PKCS11Object.CKA_EC_PARAMS] = curveasn.getBytes(); 201 var keys = this.p11session.generateKeyPair(PKCS11Session.CKM_EC_KEY_PAIR_GEN, null, pubAttr, priAttr); 202 203 var value = keys[0].getAttribute(PKCS11Object.CKA_VALUE); 204 var pk = new ASN1(value); 205 var point = pk.value.bytes(1); 206 207 prk.setComponent(Key.ECC_CURVE_OID, curve); 208 puk.setComponent(Key.ECC_QX, point.left(point.length >> 1)); 209 puk.setComponent(Key.ECC_QY, point.right(point.length >> 1)); 210 } 211 212 puk.p11 = keys[0]; 213 prk.p11 = keys[1]; 214 } 215 216 217 218 function P11Crypto(p11session) { 219 this.p11session = p11session; 220 this.crypto = new Crypto(); 221 } 222 223 224 225 P11Crypto.prototype.sign = function(key, mech, data, iv) { 226 var ckm_sign = 0; 227 var mech_hash = 0; 228 var wrap = false; 229 230 switch(mech) { 231 case Crypto.ECDSA_SHA1: 232 ckm_sign = PKCS11Session.CKM_ECDSA; 233 mech_hash = Crypto.SHA_1; 234 wrap = true; 235 break; 236 case Crypto.ECDSA_SHA224: 237 ckm_sign = PKCS11Session.CKM_ECDSA; 238 mech_hash = Crypto.SHA_224; 239 wrap = true; 240 break; 241 case Crypto.ECDSA_SHA256: 242 ckm_sign = PKCS11Session.CKM_ECDSA; 243 mech_hash = Crypto.SHA_256; 244 wrap = true; 245 break; 246 case Crypto.ECDSA_SHA384: 247 ckm_sign = PKCS11Session.CKM_ECDSA; 248 mech_hash = Crypto.SHA_384; 249 wrap = true; 250 break; 251 case Crypto.ECDSA_SHA512: 252 ckm_sign = PKCS11Session.CKM_ECDSA; 253 mech_hash = Crypto.SHA_512; 254 wrap = true; 255 break; 256 case Crypto.ECDSA: 257 ckm_sign = PKCS11Session.CKM_ECDSA; 258 wrap = true; 259 break; 260 case Crypto.RSA_SHA1: 261 ckm_sign = PKCS11Session.CKM_SHA1_RSA_PKCS; 262 break; 263 case Crypto.RSA_SHA256: 264 ckm_sign = PKCS11Session.CKM_SHA256_RSA_PKCS; 265 break; 266 case Crypto.RSA_SHA512: 267 ckm_sign = PKCS11Session.CKM_SHA512_RSA_PKCS; 268 break; 269 case Crypto.RSA_PSS_SHA1: 270 ckm_sign = PKCS11Session.CKM_SHA1_RSA_PKCS_PSS; 271 break; 272 case Crypto.RSA_PSS_SHA256: 273 ckm_sign = PKCS11Session.CKM_SHA256_RSA_PKCS_PSS; 274 break; 275 case Crypto.RSA_PSS_SHA512: 276 ckm_sign = PKCS11Session.CKM_SHA512_RSA_PKCS_PSS; 277 break; 278 default: 279 throw new GPError("P11Crypto", GPError.INVALID_ARGUMENT, 2, "Unsupported mechanism"); 280 } 281 282 if (mech_hash) { 283 data = this.crypto.digest(mech_hash, data); 284 } 285 286 // Test with single step C_Sign 287 this.p11session.signInit(ckm_sign, key.p11); 288 289 var signature = this.p11session.sign(data); 290 291 if (wrap) { 292 signature = CVC.wrapSignature(signature); 293 } 294 295 return signature; 296 } 297 298 299 300 P11Crypto.prototype.verify = function(key, mech, data, sig) { 301 return this.crypto.verify(key, mech, data, sig); 302 } 303 304 305 306 P11CVCertificateStore.testPath = GPSystem.mapFilename("testca", GPSystem.CWD); 307 308 P11CVCertificateStore.test = function() { 309 var p = new PKCS11Provider("/usr/local/lib/opensc-pkcs11.so"); 310 311 try { 312 var slots = p.getSlots(); 313 print(slots); 314 var slot = slots[1]; 315 316 print("Using slot: " + slot.getId()); 317 318 // Open R/W session 319 var s = new PKCS11Session(p, slot.getId(), true); 320 321 // Login with USER PIN 322 s.login("648219"); 323 324 var cs = new P11CVCertificateStore(P11CVCertificateStore.testPath + "/cvca", s); 325 326 var crypto = cs.getCrypto(); 327 328 var prk = new Key(); 329 prk.setType(Key.PRIVATE); 330 prk.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 331 var puk = new Key(); 332 puk.setType(Key.PUBLIC); 333 puk.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID)); 334 335 var chr = new PublicKeyReference("DE00001"); 336 cs.generateKeyPair("/test", chr, Crypto.ECC, prk, puk); 337 var msg = new ByteString("Hello World", ASCII); 338 339 var sig = crypto.sign(prk, Crypto.ECDSA_SHA1, msg); 340 assert(crypto.verify(puk, Crypto.ECDSA_SHA1, msg, sig)); 341 342 var sig = crypto.sign(prk, Crypto.ECDSA_SHA224, msg); 343 assert(crypto.verify(puk, Crypto.ECDSA_SHA224, msg, sig)); 344 345 var sig = crypto.sign(prk, Crypto.ECDSA_SHA256, msg); 346 assert(crypto.verify(puk, Crypto.ECDSA_SHA256, msg, sig)); 347 348 var sig = crypto.sign(prk, Crypto.ECDSA_SHA384, msg); 349 assert(crypto.verify(puk, Crypto.ECDSA_SHA384, msg, sig)); 350 351 var sig = crypto.sign(prk, Crypto.ECDSA_SHA512, msg); 352 assert(crypto.verify(puk, Crypto.ECDSA_SHA512, msg, sig)); 353 354 355 var cvca = new CVCCA(crypto, cs, null, null, "/UTCVCA"); 356 357 // Create a new request 358 var car = new PublicKeyReference("UTCVCA00000"); 359 360 var req = cvca.generateRequest(car, false); 361 print("Request: " + req); 362 print(req.getASN1()); 363 364 assert(req.verifyWith(crypto, req.getPublicKey())); 365 366 // Create self-signed or link certificate based on request 367 var policy = { certificateValidityDays: 2, 368 chatRoleOID: new ByteString("id-IS", OID), 369 chatRights: new ByteString("E3", HEX), 370 includeDomainParameter: true, 371 extensions: [] 372 }; 373 var cert = cvca.generateCertificate(req, policy); 374 print("Certificate: " + cert); 375 print(cert.getASN1()); 376 377 // Import certificate into store, making it the most current certificate 378 cvca.storeCertificate(cert); 379 380 // Generate additional self-signed root certificate 381 // This must be done after the link certificate has been imported 382 var policy = { certificateValidityDays: 2, 383 chatRoleOID: new ByteString("id-IS", OID), 384 chatRights: new ByteString("E3", HEX), 385 includeDomainParameter: true, 386 extensions: [] 387 }; 388 var cert = cvca.generateCertificate(req, policy); 389 print("Certificate: " + cert); 390 print(cert.getASN1()); 391 392 // Import certificate into store, making it the most current certificate 393 cvca.storeCertificate(cert); 394 } 395 finally { 396 p.cleanup(); 397 } 398 } 399