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 }