4040import java .security .spec .InvalidKeySpecException ;
4141import java .security .spec .PKCS8EncodedKeySpec ;
4242import java .security .spec .X509EncodedKeySpec ;
43+ import java .util .ArrayList ;
44+
45+ import javax .crypto .spec .DHParameterSpec ;
4346
4447import org .bouncycastle .asn1 .ASN1InputStream ;
4548import org .bouncycastle .asn1 .ASN1Primitive ;
4649import org .bouncycastle .asn1 .ASN1Sequence ;
4750import org .jruby .Ruby ;
4851import org .jruby .RubyClass ;
52+ import org .jruby .RubyException ;
53+ import org .jruby .RubyInteger ;
4954import org .jruby .RubyModule ;
5055import org .jruby .RubyObject ;
5156import org .jruby .RubyString ;
@@ -124,13 +129,16 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR
124129 pass = args [1 ].isNil () ? null : args [1 ].toString ().toCharArray ();
125130 }
126131
132+ ArrayList <Exception > exceptions = new ArrayList <>();
133+
127134 final RubyString str = readInitArg (context , data );
128135 KeyPair keyPair ;
129136 // d2i_PrivateKey_bio
130137 try {
131138 keyPair = readPrivateKey (str , pass );
132139 } catch (IOException e ) {
133140 debugStackTrace (runtime , "PKey readPrivateKey" , e ); /* ignore */
141+ exceptions .add (e );
134142 keyPair = null ;
135143 }
136144 // PEM_read_bio_PrivateKey
@@ -154,12 +162,14 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR
154162 if (pubKey != null ) return new PKeyRSA (runtime , (RSAPublicKey ) pubKey );
155163 } catch (IOException e ) {
156164 debugStackTrace (runtime , "PKey readRSAPublicKey" , e ); /* ignore */
165+ exceptions .add (e );
157166 }
158167 try {
159168 pubKey = PEMInputOutput .readDSAPublicKey (new StringReader (str .toString ()), null );
160169 if (pubKey != null ) return new PKeyDSA (runtime , (DSAPublicKey ) pubKey );
161170 } catch (IOException e ) {
162171 debugStackTrace (runtime , "PKey readDSAPublicKey" , e ); /* ignore */
172+ exceptions .add (e );
163173 }
164174
165175 final byte [] input = StringHelper .readX509PEM (context , str );
@@ -168,14 +178,27 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR
168178 pubKey = org .jruby .ext .openssl .impl .PKey .readPublicKey (input );
169179 } catch (IOException e ) {
170180 debugStackTrace (runtime , "PKey readPublicKey" , e ); /* ignore */
181+ exceptions .add (e );
171182 }
172183 // PEM_read_bio_PUBKEY
173184 if (pubKey == null ) {
174185 try {
175186 pubKey = PEMInputOutput .readPubKey (new StringReader (str .toString ()));
176187 } catch (IOException e ) {
177188 debugStackTrace (runtime , "PKey readPubKey" , e ); /* ignore */
189+ exceptions .add (e );
190+ }
191+ }
192+
193+ if (pubKey == null ) {
194+ try {
195+ final PKeyDH dh = new PKeyDH (runtime , str );
196+ return dh ;
197+ } catch (Exception e ) {
198+ debugStackTrace (runtime , "PKey read DH" , e ); /* ignore */
199+ exceptions .add (e );
178200 }
201+
179202 }
180203
181204 if (pubKey instanceof RSAPublicKey ) {
@@ -188,14 +211,35 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR
188211 return new PKeyEC (runtime , pubKey );
189212 }
190213
191- throw newPKeyError (runtime , "Could not parse PKey: unsupported" );
214+ exceptions .stream ().forEach (Throwable ::printStackTrace );
215+
216+ throw newPKeyError (runtime , "Could not parse PKey: unsupported " + pubKey + " " + exceptions );
192217 }
193218
194219 private static String getAlgorithm (final KeyPair key ) {
195220 if ( key .getPrivate () != null ) return key .getPrivate ().getAlgorithm ();
196221 if ( key .getPublic () != null ) return key .getPublic ().getAlgorithm ();
197222 return null ;
198223 }
224+
225+ @ JRubyMethod (name = "generate_key" , meta = true , required = 1 , optional = 1 )
226+ public static IRubyObject generateKey (final ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
227+ if (!(args [0 ] instanceof RubyString )) {
228+ throw context .getRuntime ().newNotImplementedError ("generate_key not implemented for " + args [0 ].getMetaClass ().getName ());
229+ }
230+
231+
232+ throw context .getRuntime ().newNotImplementedError ("generate_key not implemented for " + args [0 ].inspect ());
233+ }
234+
235+ @ JRubyMethod (name = "generate_parameters" , meta = true , required = 1 , optional = 1 )
236+ public static IRubyObject generateParameters (final ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
237+ if (!(args [0 ] instanceof RubyString )) {
238+ throw context .getRuntime ().newArgumentError ("generate_parameters requires a string argument" );
239+ }
240+
241+ throw context .getRuntime ().newNotImplementedError ("generate_parameters not implemented for " + args [0 ].inspect () + " " + args [1 ].inspect ());
242+ }
199243 }
200244
201245 public PKey (Ruby runtime , RubyClass type ) {
@@ -222,6 +266,8 @@ public IRubyObject initialize(ThreadContext context) {
222266
223267 public abstract RubyString to_pem (ThreadContext context , final IRubyObject [] args ) ;
224268
269+ public abstract RubyString oid () ;
270+
225271 @ JRubyMethod (name = "sign" )
226272 public IRubyObject sign (IRubyObject digest , IRubyObject data ) {
227273 final Ruby runtime = getRuntime ();
@@ -246,6 +292,27 @@ public ASN1Primitive toASN1PublicInfo() throws IOException {
246292 return data ;
247293 }
248294
295+ @ JRubyMethod (name = "inspect" )
296+ public IRubyObject inspect () {
297+ final Ruby runtime = getRuntime ();
298+ final StringBuilder result = new StringBuilder ();
299+ result .append ("#<" ).append (getMetaClass ().getName ()).append (" " );
300+ result .append ("oid=" ).append (this .oid ().asJavaString ());
301+ result .append (">" );
302+ return runtime .newString (result .toString ());
303+ }
304+
305+ @ JRubyMethod (name = "compare?" , required = 1 )
306+ public IRubyObject compare (ThreadContext context , IRubyObject other ) {
307+ if (other instanceof PKey ) {
308+ final PKey otherKey = (PKey ) other ;
309+ if (this .getAlgorithm ().equals (otherKey .getAlgorithm ())) {
310+ return context .runtime .newBoolean (this .to_der ().equals (otherKey .to_der ()));
311+ }
312+ }
313+ return context .runtime .getFalse ();
314+ }
315+
249316 @ Override
250317 public Object toJava (final Class target ) {
251318 if (PrivateKey .class .isAssignableFrom (target )) {
0 commit comments