1 module hunt.security.x509.X509Key; 2 3 import hunt.security.x509.AlgorithmId; 4 5 import hunt.security.Key; 6 import hunt.security.Provider; 7 8 import hunt.security.util.DerValue; 9 import hunt.security.util.DerOutputStream; 10 // import hunt.security.InvalidKeyException; 11 // import hunt.security.NoSuchAlgorithmException; 12 // import hunt.security.spec.InvalidKeySpecException; 13 // import hunt.security.spec.X509EncodedKeySpec; 14 15 import hunt.Exceptions; 16 17 import std.bitmanip; 18 19 /** 20 * Holds an X.509 key, for example a key found in an X.509 21 * certificate. Includes a description of the algorithm to be used 22 * with the key; these keys normally are used as 23 * "SubjectPublicKeyInfo". 24 * 25 * <P>While this class can represent any kind of X.509 key, it may be 26 * desirable to provide subclasses which understand how to parse keying 27 * data. For example, RSA keys have two members, one for the 28 * modulus and one for the prime exponent. If such a class is 29 * provided, it is used when parsing X.509 keys. If one is not provided, 30 * the key still parses correctly. 31 * 32 * @author David Brownell 33 */ 34 class X509Key : PublicKey { 35 36 /** use serialVersionUID from JDK 1.1. for interoperability */ 37 // private static final long serialVersionUID = -5359250853002055002L; 38 39 /* The algorithm information (name, parameters, etc). */ 40 protected AlgorithmId algid; 41 42 // /** 43 // * The key bytes, without the algorithm information. 44 // * @deprecated Use the BitArray form which does not require keys to 45 // * be byte aligned. 46 // * @see sun.security.x509.X509Key#setKey(BitArray) 47 // * @see sun.security.x509.X509Key#getKey() 48 // */ 49 // @Deprecated 50 // protected byte[] key = null; 51 52 // /* 53 // * The number of bits unused in the last byte of the key. 54 // * Added to keep the byte[] key form consistent with the BitArray 55 // * form. Can de deleted when byte[] key is deleted. 56 // */ 57 // @Deprecated 58 // private int unusedBits = 0; 59 60 // /* BitArray form of key */ 61 // private BitArray bitStringKey = null; 62 63 /* The encoding for the key. */ 64 protected byte[] encodedKey; 65 66 // /** 67 // * Default constructor. The key constructed must have its key 68 // * and algorithm initialized before it may be used, for example 69 // * by using <code>decode</code>. 70 // */ 71 // this() { } 72 73 // /* 74 // * Build and initialize as a "default" key. All X.509 key 75 // * data is stored and transmitted losslessly, but no knowledge 76 // * about this particular algorithm is available. 77 // */ 78 // private this(AlgorithmId algid, BitArray key) 79 // { 80 // this.algid = algid; 81 // setKey(key); 82 // encode(); 83 // } 84 85 // /** 86 // * Sets the key in the BitArray form. 87 // */ 88 // protected void setKey(BitArray key) { 89 // this.bitStringKey = (BitArray)key.clone(); 90 91 // /* 92 // * Do this to keep the byte array form consistent with 93 // * this. Can delete when byte[] key is deleted. 94 // */ 95 // this.key = key.toByteArray(); 96 // int remaining = key.length() % 8; 97 // this.unusedBits = 98 // ((remaining == 0) ? 0 : 8 - remaining); 99 // } 100 101 /** 102 * Gets the key. The key may or may not be byte aligned. 103 * @return a BitArray containing the key. 104 */ 105 protected BitArray getKey() { 106 /* 107 * Do this for consistency in case a subclass 108 * modifies byte[] key directly. Remove when 109 * byte[] key is deleted. 110 * Note: the consistency checks fail when the subclass 111 * modifies a non byte-aligned key (into a byte-aligned key) 112 * using the deprecated byte[] key field. 113 */ 114 // this.bitStringKey = new BitArray( 115 // this.key.length * 8 - this.unusedBits, 116 // this.key); 117 118 // return (BitArray)bitStringKey.clone(); 119 implementationMissing(); 120 return BitArray.init; 121 } 122 123 /** 124 * Construct X.509 subject key from a DER value. If 125 * the runtime environment is configured with a specific class for 126 * this kind of key, a subclass is returned. Otherwise, a generic 127 * X509Key object is returned. 128 * 129 * <P>This mechanism gurantees that keys (and algorithms) may be 130 * freely manipulated and transferred, without risk of losing 131 * information. Also, when a key (or algorithm) needs some special 132 * handling, that specific need can be accomodated. 133 * 134 * @param in the DER-encoded SubjectPublicKeyInfo value 135 * @exception IOException on data format errors 136 */ 137 static PublicKey parse(DerValue value) 138 { 139 AlgorithmId algorithm; 140 PublicKey subjectKey; 141 142 // if (value.tag != DerValue.tag_Sequence) 143 // throw new IOException("corrupt subject key"); 144 145 // algorithm = AlgorithmId.parse(value.data.getDerValue()); 146 // try { 147 // subjectKey = buildX509Key(algorithm, 148 // value.data.getUnalignedBitString()); 149 150 // } catch (InvalidKeyException e) { 151 // throw new IOException("subject key, " ~ e.msg, e); 152 // } 153 154 // if (value.data.available() != 0) 155 // throw new IOException("excess subject key"); 156 implementationMissing(); 157 return subjectKey; 158 } 159 160 // /** 161 // * Parse the key bits. This may be redefined by subclasses to take 162 // * advantage of structure within the key. For example, RSA public 163 // * keys encapsulate two unsigned integers (modulus and exponent) as 164 // * DER values within the <code>key</code> bits; Diffie-Hellman and 165 // * DSS/DSA keys encapsulate a single unsigned integer. 166 // * 167 // * <P>This function is called when creating X.509 SubjectPublicKeyInfo 168 // * values using the X509Key member functions, such as <code>parse</code> 169 // * and <code>decode</code>. 170 // * 171 // * @exception IOException on parsing errors. 172 // * @exception InvalidKeyException on invalid key encodings. 173 // */ 174 // protected void parseKeyBits(), InvalidKeyException { 175 // encode(); 176 // } 177 178 // /* 179 // * Factory interface, building the kind of key associated with this 180 // * specific algorithm ID or else returning this generic base class. 181 // * See the description above. 182 // */ 183 // static PublicKey buildX509Key(AlgorithmId algid, BitArray key) 184 // , InvalidKeyException 185 // { 186 // /* 187 // * Use the algid and key parameters to produce the ASN.1 encoding 188 // * of the key, which will then be used as the input to the 189 // * key factory. 190 // */ 191 // DerOutputStream x509EncodedKeyStream = new DerOutputStream(); 192 // encode(x509EncodedKeyStream, algid, key); 193 // X509EncodedKeySpec x509KeySpec 194 // = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); 195 196 // try { 197 // // Instantiate the key factory of the appropriate algorithm 198 // KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); 199 200 // // Generate the key 201 // return keyFac.generatePublic(x509KeySpec); 202 // } catch (NoSuchAlgorithmException e) { 203 // // Return generic X509Key with opaque key data (see below) 204 // } catch (InvalidKeySpecException e) { 205 // throw new InvalidKeyException(e.msg, e); 206 // } 207 208 // /* 209 // * Try again using JDK1.1-style for backwards compatibility. 210 // */ 211 // string classname = ""; 212 // try { 213 // Properties props; 214 // string keytype; 215 // Provider sunProvider; 216 217 // sunProvider = Security.getProvider("SUN"); 218 // if (sunProvider == null) 219 // throw new InstantiationException(); 220 // classname = sunProvider.getProperty("PublicKey.X.509." ~ 221 // algid.getName()); 222 // if (classname == null) { 223 // throw new InstantiationException(); 224 // } 225 226 // Class<?> keyClass = null; 227 // try { 228 // keyClass = Class.forName(classname); 229 // } catch (ClassNotFoundException e) { 230 // ClassLoader cl = ClassLoader.getSystemClassLoader(); 231 // if (cl != null) { 232 // keyClass = cl.loadClass(classname); 233 // } 234 // } 235 236 // @SuppressWarnings("deprecation") 237 // Object inst = (keyClass != null) ? keyClass.newInstance() : null; 238 // X509Key result; 239 240 // if (inst instanceof X509Key) { 241 // result = (X509Key) inst; 242 // result.algid = algid; 243 // result.setKey(key); 244 // result.parseKeyBits(); 245 // return result; 246 // } 247 // } catch (ClassNotFoundException e) { 248 // } catch (InstantiationException e) { 249 // } catch (IllegalAccessException e) { 250 // // this should not happen. 251 // throw new IOException (classname + " [internal error]"); 252 // } 253 254 // X509Key result = new X509Key(algid, key); 255 // return result; 256 // } 257 258 /** 259 * Returns the algorithm to be used with this key. 260 */ 261 string getAlgorithm() { 262 return algid.getName(); 263 } 264 265 /** 266 * Returns the algorithm ID to be used with this key. 267 */ 268 AlgorithmId getAlgorithmId() { return algid; } 269 270 /** 271 * Encode SubjectPublicKeyInfo sequence on the DER output stream. 272 * 273 * @exception IOException on encoding errors. 274 */ 275 final void encode(DerOutputStream stream) 276 { 277 encode(stream, this.algid, getKey()); 278 } 279 280 /** 281 * Returns the DER-encoded form of the key as a byte array. 282 */ 283 byte[] getEncoded() { 284 try { 285 return getEncodedInternal().dup; 286 } catch (InvalidKeyException e) { 287 // XXX 288 } 289 return null; 290 } 291 292 byte[] getEncodedInternal() { 293 byte[] encoded = encodedKey; 294 if (encoded == null) { 295 try { 296 DerOutputStream stream = new DerOutputStream(); 297 encode(stream); 298 encoded = stream.toByteArray(); 299 } catch (IOException e) { 300 throw new InvalidKeyException("IOException : " ~ 301 e.msg); 302 } 303 encodedKey = encoded; 304 } 305 return encoded; 306 } 307 308 /** 309 * Returns the format for this key: "X.509" 310 */ 311 string getFormat() { 312 return "X.509"; 313 } 314 315 /** 316 * Returns the DER-encoded form of the key as a byte array. 317 * 318 * @exception InvalidKeyException on encoding errors. 319 */ 320 byte[] encode() { 321 return getEncodedInternal().dup; 322 } 323 324 /* 325 * Returns a printable representation of the key 326 */ 327 // string toString() 328 // { 329 // HexDumpEncoder encoder = new HexDumpEncoder(); 330 331 // return "algorithm = " ~ algid.toString() 332 // + ", unparsed keybits = \n" ~ encoder.encodeBuffer(key); 333 // } 334 335 // /** 336 // * Initialize an X509Key object from an input stream. The data on that 337 // * input stream must be encoded using DER, obeying the X.509 338 // * <code>SubjectPublicKeyInfo</code> format. That is, the data is a 339 // * sequence consisting of an algorithm ID and a bit string which holds 340 // * the key. (That bit string is often used to encapsulate another DER 341 // * encoded sequence.) 342 // * 343 // * <P>Subclasses should not normally redefine this method; they should 344 // * instead provide a <code>parseKeyBits</code> method to parse any 345 // * fields inside the <code>key</code> member. 346 // * 347 // * <P>The exception to this rule is that since private keys need not 348 // * be encoded using the X.509 <code>SubjectPublicKeyInfo</code> format, 349 // * private keys may override this method, <code>encode</code>, and 350 // * of course <code>getFormat</code>. 351 // * 352 // * @param in an input stream with a DER-encoded X.509 353 // * SubjectPublicKeyInfo value 354 // * @exception InvalidKeyException on parsing errors. 355 // */ 356 // void decode(InputStream in) 357 // 358 // { 359 // DerValue val; 360 361 // try { 362 // val = new DerValue(in); 363 // if (val.tag != DerValue.tag_Sequence) 364 // throw new InvalidKeyException("invalid key format"); 365 366 // algid = AlgorithmId.parse(val.data.getDerValue()); 367 // setKey(val.data.getUnalignedBitString()); 368 // parseKeyBits(); 369 // if (val.data.available() != 0) 370 // throw new InvalidKeyException ("excess key data"); 371 372 // } catch (IOException e) { 373 // throw new InvalidKeyException("IOException: " ~ 374 // e.msg); 375 // } 376 // } 377 378 // void decode(byte[] encodedKey) { 379 // decode(new ByteArrayInputStream(encodedKey)); 380 // } 381 382 // /** 383 // * Serialization write ... X.509 keys serialize as 384 // * themselves, and they're parsed when they get read back. 385 // */ 386 // private void writeObject(ObjectOutputStream stream) { 387 // stream.write(getEncoded()); 388 // } 389 390 // /** 391 // * Serialization read ... X.509 keys serialize as 392 // * themselves, and they're parsed when they get read back. 393 // */ 394 // private void readObject(ObjectInputStream stream) { 395 // try { 396 // decode(stream); 397 // } catch (InvalidKeyException e) { 398 // e.printStackTrace(); 399 // throw new IOException("deserialized key is invalid: " ~ 400 // e.msg); 401 // } 402 // } 403 404 // boolean equals(Object obj) { 405 // if (this == obj) { 406 // return true; 407 // } 408 // if (obj instanceof Key == false) { 409 // return false; 410 // } 411 // try { 412 // byte[] thisEncoded = this.getEncodedInternal(); 413 // byte[] otherEncoded; 414 // if (obj instanceof X509Key) { 415 // otherEncoded = ((X509Key)obj).getEncodedInternal(); 416 // } else { 417 // otherEncoded = ((Key)obj).getEncoded(); 418 // } 419 // return Arrays.equals(thisEncoded, otherEncoded); 420 // } catch (InvalidKeyException e) { 421 // return false; 422 // } 423 // } 424 425 // /** 426 // * Calculates a hash code value for the object. Objects 427 // * which are equal will also have the same hashcode. 428 // */ 429 // int hashCode() { 430 // try { 431 // byte[] b1 = getEncodedInternal(); 432 // int r = b1.length; 433 // for (int i = 0; i < b1.length; i++) { 434 // r += (b1[i] & 0xff) * 37; 435 // } 436 // return r; 437 // } catch (InvalidKeyException e) { 438 // // should not happen 439 // return 0; 440 // } 441 // } 442 443 /* 444 * Produce SubjectPublicKey encoding from algorithm id and key material. 445 */ 446 static void encode(DerOutputStream stream, AlgorithmId algid, BitArray key) 447 { 448 DerOutputStream tmp = new DerOutputStream(); 449 algid.encode(tmp); 450 tmp.putUnalignedBitString(key); 451 stream.write(DerValue.tag_Sequence, tmp); 452 } 453 }