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 Basic helper functions to convert PKCS#8 data to GP keys and vice versa
 25  */
 26 
 27 
 28 
 29 /**
 30  * Empty constructor
 31  */
 32 function PKCS8() {
 33 }
 34 
 35 if (typeof(exports) != "undefined")
 36 	exports.PKCS8 = PKCS8;
 37 
 38 
 39 PKCS8.idEcPublicKey = new ByteString("id-ecPublicKey", OID);
 40 PKCS8.rsaEncryption = new ByteString("1.2.840.113549.1.1.1", OID);
 41 
 42 /**
 43  * Convert x/y coordinates to uncompressed format
 44  *
 45  * @param {ByteString} x the x coordinate
 46  * @param {ByteString} y the y coordinate
 47  * @type ByteString
 48  * @return ByteString containing compressed format
 49  *
 50  */
 51 PKCS8.encodeUncompressedECPoint = function(x,y) {
 52 
 53 	var bb = new ByteBuffer();
 54 
 55 	// uncompressed encoding
 56 	bb.append(new ByteString("04", HEX));
 57 	bb.append(x);
 58 	bb.append(y);
 59 
 60 	return bb.toByteString();
 61 }
 62 
 63 
 64 
 65 /**
 66  * Convert uncompressed format to x and y coordinates
 67  *
 68  * @param {ByteString} compressed point
 69  * @type Object
 70  * @return Object with ByteString properties x and y
 71  *
 72  */
 73 PKCS8.decodeUncompressedECPoint = function(uncompressedPoint) {
 74 
 75 	// Determine the size of the coordinates ignoring the indicator byte '04'
 76 	var length = uncompressedPoint.length - 1;
 77 
 78 	var sizeOfCoordinate = length >> 1;
 79 
 80 	var xValue = uncompressedPoint.bytes(1, sizeOfCoordinate);
 81 	var yValue = uncompressedPoint.bytes(1 + sizeOfCoordinate, sizeOfCoordinate);
 82 
 83 	return { x:xValue, y:yValue };
 84 }
 85 
 86 
 87 
 88 /**
 89  * Integer to octet string conversion
 90  *
 91  * @param {ByteString} value the encoded integer value
 92  * @param {Number} the number of digits
 93  * @type ByteString
 94  * @return the truncated or padded result
 95  */
 96 PKCS8.I2O = function(value, length) {
 97 	if (value.length > length) {
 98 		value = value.right(length);
 99 	}
100 	while (value.length < length) {
101 		value = PKCS8.PAD.left((length - value.length - 1 & 15) + 1).concat(value);
102 	}
103 	return value;
104 }
105 PKCS8.PAD = new ByteString("00000000000000000000000000000000", HEX);
106 
107 
108 
109 /**
110  * Strips leading zeros of a ByteString
111  *
112  * @param {ByteString} value the ByteString value
113  * @return the stripped ByteString object, may be an empty ByteString
114  * @type ByteString
115  */
116 PKCS8.stripLeadingZeros = function(value, size) {
117 	if (typeof(size) == "undefined") {
118 		var limit = value.length;
119 	} else {
120 		limit = value.length - size;
121 	}
122 
123 	var i = 0;
124 	for (; (i < limit) && (value.byteAt(i) == 0); i++);
125 
126 	return value.bytes(i);
127 }
128 
129 
130 
131 /**
132  * Removes leading zeros and prepends a single '00' to ByteStrings which have the most significant bit set.
133  *
134  * This prevent interpretation of the integer representation if converted into
135  * a signed ASN1 INTEGER.
136  *
137  * @param {ByteString} value the value to convert
138  * @return the converted value
139  * @type ByteString
140  */
141 PKCS8.convertUnsignedInteger = function(value) {
142 	assert(value.length > 0);
143 
144 	var i = 0;
145 	for (; (i < value.length - 1) && (value.byteAt(i) == 0); i++);
146 
147 	if (value.byteAt(i) >= 0x80) {
148 		value = (new ByteString("00", HEX)).concat(value.bytes(i));
149 	} else {
150 		value = value.bytes(i);
151 	}
152 
153 	return value;
154 }
155 
156 
157 
158 /**
159  * Encode a given GP ECC private key as specified by the PKCS#8 format
160  *
161  * @param {Key} the private key object that should be encoded
162  * @return the encoded PKCS#8 private key
163  * @type ByteString
164  */
165 PKCS8.encodeECCKeyUsingPKCS8Format = function(privateKey) {
166 	var privateKeyInfo = new ASN1(ASN1.SEQUENCE);
167 
168 	// Set the version number - must be zero
169 	privateKeyInfo.add(new ASN1(ASN1.INTEGER, new ByteString("00", HEX)));
170 
171 	var privateKeyAlgorithm = new ASN1(ASN1.SEQUENCE);
172 	privateKeyAlgorithm.add(new ASN1(ASN1.OBJECT_IDENTIFIER, PKCS8.idEcPublicKey));
173 
174 	var domainInfo = new ASN1(ASN1.SEQUENCE);
175 
176 	// Cofactor - must be 1
177 	domainInfo.add(new ASN1(ASN1.INTEGER, PKCS8.stripLeadingZeros(privateKey.getComponent(Key.ECC_H))));
178 
179 	var field = new ASN1(ASN1.SEQUENCE);
180 
181 	// we are using a prime field
182 	field.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("prime-field", OID))); // prime field
183 
184 	var primeOrder = privateKey.getComponent(Key.ECC_P);
185 	if (primeOrder.byteAt(0) >= 0x80) { // signed int? -> add 0x00
186 		field.add(new ASN1(ASN1.INTEGER, new ByteString("00", HEX).concat(privateKey.getComponent(Key.ECC_P))));
187 	} else {
188 		field.add(new ASN1(ASN1.INTEGER, privateKey.getComponent(Key.ECC_P)));
189 	}
190 
191 	domainInfo.add(field);
192 
193 	// Coefficients a and b
194 	var coeff = new ASN1(ASN1.SEQUENCE);
195 
196 	// first coefficient
197 	coeff.add(new ASN1(ASN1.OCTET_STRING, privateKey.getComponent(Key.ECC_A)));
198 
199 	// second coefficient
200 	coeff.add(new ASN1(ASN1.OCTET_STRING, privateKey.getComponent(Key.ECC_B)));
201 
202 	domainInfo.add(coeff);
203 
204 	// Base point (uncompressed)
205 	var gx = privateKey.getComponent(Key.ECC_GX);
206 	var gy = privateKey.getComponent(Key.ECC_GY);
207 
208 	domainInfo.add(new ASN1(ASN1.OCTET_STRING, PKCS8.encodeUncompressedECPoint(gx, gy)));
209 
210 	// group order generated by the base point
211 	var groupOrder = privateKey.getComponent(Key.ECC_N);
212 	if (groupOrder.byteAt(0) >= 0x80) { // signed int? -> add 0x00
213 		domainInfo.add(new ASN1(ASN1.INTEGER, new ByteString("00", HEX).concat(privateKey.getComponent(Key.ECC_N))));
214 	} else {
215 		domainInfo.add(new ASN1(ASN1.INTEGER, privateKey.getComponent(Key.ECC_N)));
216 	}
217 
218 	privateKeyAlgorithm.add(domainInfo);
219 
220 	// encode the key information
221 	privateKeyInfo.add(privateKeyAlgorithm);
222 
223 	// encode the private key
224 	var encodedPrivateKey = new ASN1(ASN1.OCTET_STRING);
225 
226 	var pk = privateKey.getComponent(Key.ECC_D);
227 	var key = new ASN1(ASN1.SEQUENCE);
228 	key.add(new ASN1(ASN1.INTEGER, new ByteString("01", HEX)));
229 	key.add(new ASN1(ASN1.OCTET_STRING, pk));
230 
231 	encodedPrivateKey.add(key);
232 
233 	privateKeyInfo.add(encodedPrivateKey);
234 
235 //	print(privateKeyInfo);
236 	return privateKeyInfo.getBytes();
237 }
238 
239 
240 
241 /**
242  * Encode RSA private key as defined in PKCS#1
243  *
244  * RSAPrivateKey ::= SEQUENCE {
245  *     version           Version,
246  *     modulus           INTEGER,  -- n
247  *     publicExponent    INTEGER,  -- e
248  *     privateExponent   INTEGER,  -- d
249  *     prime1            INTEGER,  -- p
250  *     prime2            INTEGER,  -- q
251  *     exponent1         INTEGER,  -- d mod (p-1)
252  *     exponent2         INTEGER,  -- d mod (q-1)
253  *     coefficient       INTEGER,  -- (inverse of q) mod p
254  *     otherPrimeInfos   OtherPrimeInfos OPTIONAL
255  * }
256  * @param {Key} privateKey the private RSA key in CRT format
257  * @type ByteString
258  * @return the encoded RSA key
259  */
260 PKCS8.encodeRSAKey = function(privateKey, publicKey) {
261 	var rsaPrivateKey =
262 		new ASN1(ASN1.SEQUENCE);
263 
264 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(0)));
265 	if (typeof(publicKey) != "undefined") {
266 		rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(publicKey.getComponent(Key.MODULUS))));
267 		rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(publicKey.getComponent(Key.EXPONENT))));
268 	} else {
269 		rsaPrivateKey.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(0)));
270 		rsaPrivateKey.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(0)));
271 	}
272 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, ByteString.valueOf(0)));		// Private Exponent not at interface for CRT format
273 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(privateKey.getComponent(Key.CRT_P))));
274 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(privateKey.getComponent(Key.CRT_Q))));
275 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(privateKey.getComponent(Key.CRT_DP1))));
276 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(privateKey.getComponent(Key.CRT_DQ1))));
277 	rsaPrivateKey.add(new ASN1(ASN1.INTEGER, PKCS8.convertUnsignedInteger(privateKey.getComponent(Key.CRT_PQ))));
278 
279 	return rsaPrivateKey.getBytes();
280 }
281 
282 
283 
284 /**
285  * Encode a given GP RSA private key as specified by the PKCS#8 format
286  *
287  * @param {Key} the private key object that should be encoded
288  * @return the encoded PKCS#8 private key
289  * @type ByteString
290  */
291 PKCS8.encodeRSAKeyUsingPKCS8Format = function(privateKey, publicKey) {
292 	var privateKeyInfo = new ASN1(ASN1.SEQUENCE);
293 
294 	// Set the version number - must be zero
295 	privateKeyInfo.add(new ASN1(ASN1.INTEGER, new ByteString("00", HEX)));
296 
297 	var privateKeyAlgorithm = new ASN1(ASN1.SEQUENCE);
298 	privateKeyAlgorithm.add(new ASN1(ASN1.OBJECT_IDENTIFIER, PKCS8.rsaEncryption));
299 	privateKeyAlgorithm.add(new ASN1(ASN1.NULL));
300 
301 	// encode the key information
302 	privateKeyInfo.add(privateKeyAlgorithm);
303 
304 	// encode the private key
305 	var encodedPrivateKey = new ASN1(ASN1.OCTET_STRING, PKCS8.encodeRSAKey(privateKey, publicKey));
306 
307 	privateKeyInfo.add(encodedPrivateKey);
308 
309 //	print(privateKeyInfo);
310 	return privateKeyInfo.getBytes();
311 }
312 
313 
314 
315 /**
316  * Encode a given GP private key as specified by the PKCS#8 format
317  *
318  * For now we only support the encoding of ECC private keys in a prime field
319  *
320  * @param {Key} the private key object that should be encoded
321  * @return the encoded PKCS#8 private key
322  * @type ByteString
323  */
324 PKCS8.encodeKeyUsingPKCS8Format = function(privateKey, publicKey) {
325 
326 	assert(privateKey.getType() == Key.PRIVATE);
327 	if (typeof(privateKey.getComponent(Key.ECC_P)) != "undefined") {
328 		return PKCS8.encodeECCKeyUsingPKCS8Format(privateKey);
329 	} else {
330 		return PKCS8.encodeRSAKeyUsingPKCS8Format(privateKey, publicKey);
331 	}
332 }
333 
334 
335 
336 /**
337  * Decode a given PKCS#8 ECC private key from the given TLV objects and create a GP key object
338  *
339  * For now we only support the decoding of ECC private keys in a prime field
340  *
341  * @param {ASN1} algparam the algorithm parameter from AlgorithmInfo
342  * @param {ASN1} privateKey the privateKey element from the PKCS#8 structure
343  * @return the GP key object
344  * @type Key
345  */
346 PKCS8.decodeECCKeyFromPKCS8Format = function(domainParameter, encodedKey) {
347 
348 	var key = new Key();
349 
350 	key.setType(Key.PRIVATE);
351 
352 	if (domainParameter.tag == ASN1.OBJECT_IDENTIFIER) {
353 		key.setComponent(Key.ECC_CURVE_OID, domainParameter.value);
354 	} else {
355 		// Decode the domain parameters
356 		var cofactor = domainParameter.get(0);
357 		key.setComponent(Key.ECC_H, cofactor.value);
358 
359 		var order = domainParameter.get(1).get(1);
360 		key.setComponent(Key.ECC_P, PKCS8.stripLeadingZeros(order.value));
361 
362 		var coeff_A = domainParameter.get(2).get(0);
363 		key.setComponent(Key.ECC_A, coeff_A.value);
364 
365 		var coeff_B = domainParameter.get(2).get(1);
366 		key.setComponent(Key.ECC_B, coeff_B.value);
367 
368 		var generatorPoint = domainParameter.get(3).value;
369 
370 		var coordinates = PKCS8.decodeUncompressedECPoint(generatorPoint);
371 
372 		key.setComponent(Key.ECC_GX, coordinates.x);
373 		key.setComponent(Key.ECC_GY, coordinates.y);
374 
375 		var groupOrder = domainParameter.get(4);
376 
377 		key.setComponent(Key.ECC_N, PKCS8.stripLeadingZeros(groupOrder.value));
378 	}
379 
380 	key.setComponent(Key.ECC_D, encodedKey.get(1).value);
381 
382 	return key;
383 }
384 
385 
386 
387 /**
388  * Decode a PKCS#1 encoded RSA private key from the given TLV objects and create a GP key object with the private key
389  *
390  * @param {ASN1} algparam the algorithm parameter from AlgorithmInfo
391  * @param {ASN1} privateKey the privateKey element from the PKCS#8 structure
392  * @return the GP key object
393  * @type Key
394  */
395 PKCS8.decodeRSAPrivateKeyFromPKCS1Format = function(privateKey) {
396 
397 	var key = new Key();
398 
399 	key.setType(Key.PRIVATE);
400 
401 	assert(privateKey.tag == ASN1.SEQUENCE);
402 	assert(privateKey.isconstructed);
403 	assert(privateKey.elements >= 9);
404 
405 	for (var i = 0; i < 9; i++) {
406 		var e = privateKey.get(i);
407 		assert(e.tag == ASN1.INTEGER);
408 		assert(!e.isconstructed);
409 	}
410 
411 	assert(privateKey.get(0).value.toUnsigned() == 0);
412 
413 	var p = PKCS8.stripLeadingZeros(privateKey.get(4).value);
414 	var l = p.length;
415 	key.setComponent(Key.CRT_P,   p);
416 	key.setComponent(Key.CRT_Q,   PKCS8.I2O(privateKey.get(5).value, l));
417 	key.setComponent(Key.CRT_DP1, PKCS8.I2O(privateKey.get(6).value, l));
418 	key.setComponent(Key.CRT_DQ1, PKCS8.I2O(privateKey.get(7).value, l));
419 	key.setComponent(Key.CRT_PQ,  PKCS8.I2O(privateKey.get(8).value, l));
420 
421 	return key;
422 }
423 
424 
425 
426 /**
427  * Decode a PKCS#1 encoded RSA private key from the given TLV objects and create a GP key object with the public key
428  *
429  * @param {ASN1} algparam the algorithm parameter from AlgorithmInfo
430  * @param {ASN1} privateKey the privateKey element from the PKCS#8 structure
431  * @return the GP key object
432  * @type Key
433  */
434 PKCS8.decodeRSAPublicKeyFromPKCS1Format = function(privateKey) {
435 
436 	var key = new Key();
437 
438 	key.setType(Key.MODULUS);
439 
440 	assert(privateKey.tag == ASN1.SEQUENCE);
441 	assert(privateKey.isconstructed);
442 	assert(privateKey.elements >= 9);
443 
444 	for (var i = 0; i < 9; i++) {
445 		var e = privateKey.get(i);
446 		assert(e.tag == ASN1.INTEGER);
447 		assert(!e.isconstructed);
448 	}
449 
450 	assert(privateKey.get(0).value.toUnsigned() == 0);
451 
452 	key.setComponent(Key.MODULUS,  PKCS8.stripLeadingZeros(privateKey.get(1).value));
453 	key.setComponent(Key.EXPONENT, PKCS8.stripLeadingZeros(privateKey.get(2).value));
454 
455 	return key;
456 }
457 
458 
459 
460 /**
461  * Decode a given PKCS#8 RSA private key from the given TLV objects and create a GP key object
462  *
463  * @param {ASN1} algparam the algorithm parameter from AlgorithmInfo
464  * @param {ASN1} privateKey the privateKey element from the PKCS#8 structure
465  * @return the GP key object
466  * @type Key
467  */
468 PKCS8.decodeRSAKeyFromPKCS8Format = function(algparam, privateKey) {
469 
470 	var key = new Key();
471 
472 	key.setType(Key.PRIVATE);
473 
474 	assert(algparam.tag == ASN1.NULL);
475 	assert(!algparam.isconstructed);
476 	assert(algparam.length == 0);
477 
478 	return PKCS8.decodeRSAPrivateKeyFromPKCS1Format(privateKey);
479 }
480 
481 
482 
483 /**
484  * Decode a given PKCS#8 private key from the given ByteString and create a GP key object
485  *
486  * For now we only support the decoding of ECC private keys in a prime field
487  *
488  * @param {ByteString} the private key object in PKCS#8 format
489  * @return the GP key object
490  * @type Key
491  */
492 PKCS8.decodeKeyFromPKCS8Format = function(encodedKey) {
493 	var p8 = new ASN1(encodedKey);
494 
495 	assert(p8.isconstructed);
496 	assert(p8.elements >= 3);
497 
498 	var version = p8.get(0);
499 	assert(version.tag == ASN1.INTEGER);
500 	assert(version.value.toUnsigned() == 0);
501 
502 	var pkai = p8.get(1);
503 	assert(pkai.tag == ASN1.SEQUENCE);
504 	assert(pkai.isconstructed);
505 	assert(pkai.elements == 2);
506 	var keytype = pkai.get(0);
507 
508 	assert(keytype.tag == ASN1.OBJECT_IDENTIFIER);
509 
510 	var algparam = pkai.get(1);
511 
512 	var privateKey = p8.get(2);
513 	assert(privateKey.tag == ASN1.OCTET_STRING);
514 	if (privateKey.isconstructed) {
515 		privateKey = privateKey.get(0);
516 	} else {
517 		privateKey = new ASN1(privateKey.value);
518 	}
519 
520 	if (keytype.value.equals(PKCS8.rsaEncryption)) {
521 		return PKCS8.decodeRSAKeyFromPKCS8Format(algparam, privateKey);
522 	} else if (keytype.value.equals(PKCS8.idEcPublicKey)) {
523 		return PKCS8.decodeECCKeyFromPKCS8Format(algparam, privateKey);
524 	} else {
525 		throw new Error("Unknown key type " + keytype.value.toString(OID));
526 	}
527 }
528 
529 
530 
531 /**
532  * Simple self-test
533  */
534 PKCS8.test = function() {
535 
536 	// Set OID for EC curve
537 	var ecCurve = "1.3.36.3.3.2.8.1.1.7";
538 
539     var crypto = new Crypto("BC");
540 
541     // Create empty public key object
542     var pubKey = new Key();
543     pubKey.setType(Key.PUBLIC);
544     pubKey.setComponent(Key.ECC_CURVE_OID, new ByteString(ecCurve, OID));
545 
546     // Create empty private key object
547     var priKey = new Key();
548     priKey.setType(Key.PRIVATE);
549     priKey.setComponent(Key.ECC_CURVE_OID, new ByteString(ecCurve, OID));
550 
551     // Generate key pair
552     crypto.generateKeyPair(Crypto.EC, pubKey, priKey);
553 
554     // Encode
555     var p8Key = PKCS8.encodeKeyUsingPKCS8Format(priKey);
556 
557     // Decode
558     var decodedKeyObject = PKCS8.decodeKeyFromPKCS8Format(p8Key);
559 
560     // Compare
561     assert(decodedKeyObject.getComponent(Key.ECC_D).equals(priKey.getComponent(Key.ECC_D)));
562 
563     assert(decodedKeyObject.getComponent(Key.ECC_GX).equals(priKey.getComponent(Key.ECC_GX)));
564     assert(decodedKeyObject.getComponent(Key.ECC_GY).equals(priKey.getComponent(Key.ECC_GY)));
565     assert(decodedKeyObject.getComponent(Key.ECC_A).equals(pubKey.getComponent(Key.ECC_A)));
566     assert(decodedKeyObject.getComponent(Key.ECC_B).equals(pubKey.getComponent(Key.ECC_B)));
567 
568     // Encode
569     var refp8Key = PKCS8.encodeKeyUsingPKCS8Format(decodedKeyObject);
570 
571     // Compare
572     assert(p8Key.equals(refp8Key));
573 
574 
575     // Create empty public key object
576 	var pubKey = new Key();
577     pubKey.setType(Key.PUBLIC);
578     pubKey.setSize(1024);
579 
580     // Create empty private key object
581     var priKey = new Key();
582     priKey.setType(Key.PRIVATE);
583 
584     // Generate key pair
585     crypto.generateKeyPair(Crypto.RSA, pubKey, priKey);
586 
587     // Encode
588     var p8Key = PKCS8.encodeKeyUsingPKCS8Format(priKey);
589 
590     // Decode
591     var decodedKeyObject = PKCS8.decodeKeyFromPKCS8Format(p8Key);
592 
593     // Compare
594     assert(decodedKeyObject.getComponent(Key.CRT_P).equals(priKey.getComponent(Key.CRT_P)));
595     assert(decodedKeyObject.getComponent(Key.CRT_Q).equals(priKey.getComponent(Key.CRT_Q)));
596     assert(decodedKeyObject.getComponent(Key.CRT_DP1).equals(priKey.getComponent(Key.CRT_DP1)));
597     assert(decodedKeyObject.getComponent(Key.CRT_DQ1).equals(priKey.getComponent(Key.CRT_DQ1)));
598     assert(decodedKeyObject.getComponent(Key.CRT_PQ).equals(priKey.getComponent(Key.CRT_PQ)));
599 
600     // Encode
601     var refp8Key = PKCS8.encodeKeyUsingPKCS8Format(decodedKeyObject);
602 
603     // Compare
604     assert(p8Key.equals(refp8Key));
605 }
606