12
12
import java .io .StringWriter ;
13
13
import java .math .BigInteger ;
14
14
import java .security .GeneralSecurityException ;
15
+ import java .security .InvalidKeyException ;
15
16
import java .security .KeyFactory ;
16
17
import java .security .KeyPair ;
17
18
import java .security .KeyPairGenerator ;
29
30
import java .security .spec .ECPublicKeySpec ;
30
31
import java .security .spec .EllipticCurve ;
31
32
import java .security .spec .InvalidKeySpecException ;
33
+ import java .util .Collections ;
32
34
import java .util .Enumeration ;
33
- import java .util .logging . Level ;
34
- import java . util . logging . Logger ;
35
+ import java .util .List ;
36
+ import javax . crypto . KeyAgreement ;
35
37
import org .bouncycastle .asn1 .ASN1EncodableVector ;
36
38
import org .bouncycastle .asn1 .ASN1Integer ;
37
39
import org .bouncycastle .asn1 .ASN1ObjectIdentifier ;
40
+ import org .bouncycastle .asn1 .ASN1OutputStream ;
38
41
import org .bouncycastle .asn1 .DLSequence ;
39
42
40
- import org .bouncycastle .crypto .params .ECDomainParameters ;
41
- import org .bouncycastle .jcajce .provider .asymmetric .util .ECUtil ;
42
- //import org.bouncycastle.jce.interfaces.ECPublicKey;
43
- //import org.bouncycastle.jce.interfaces.ECPrivateKey;
43
+ import org .bouncycastle .crypto .params .ECNamedDomainParameters ;
44
+ import org .bouncycastle .crypto .params .ECPrivateKeyParameters ;
45
+ import org .bouncycastle .crypto .signers .ECDSASigner ;
44
46
import org .bouncycastle .jce .ECNamedCurveTable ;
45
47
import org .bouncycastle .jce .ECPointUtil ;
46
48
import org .bouncycastle .jce .spec .ECNamedCurveParameterSpec ;
47
49
import org .bouncycastle .jce .spec .ECNamedCurveSpec ;
48
- //import org.bouncycastle.jce.spec.ECParameterSpec;
49
- //import org.bouncycastle.jce.spec.ECPublicKeySpec;
50
- //import org.bouncycastle.jce.spec.ECPrivateKeySpec;
51
- //import org.bouncycastle.math.ec.ECPoint;
52
50
53
51
import org .jruby .Ruby ;
54
52
import org .jruby .RubyArray ;
60
58
import org .jruby .anno .JRubyClass ;
61
59
import org .jruby .anno .JRubyMethod ;
62
60
import org .jruby .exceptions .RaiseException ;
63
- import org .jruby .ext .openssl .x509store .PEMInputOutput ;
64
61
import org .jruby .runtime .Arity ;
65
62
import org .jruby .runtime .ObjectAllocator ;
66
63
import org .jruby .runtime .ThreadContext ;
67
- import org .jruby .runtime .builtin .IRubyObject ;
68
64
import org .jruby .runtime .Visibility ;
65
+ import org .jruby .runtime .builtin .IRubyObject ;
66
+ import org .jruby .runtime .component .VariableEntry ;
69
67
70
68
import org .jruby .ext .openssl .impl .CipherSpec ;
71
69
import static org .jruby .ext .openssl .OpenSSL .debug ;
72
70
import static org .jruby .ext .openssl .OpenSSL .debugStackTrace ;
73
71
import static org .jruby .ext .openssl .PKey ._PKey ;
74
72
import org .jruby .ext .openssl .impl .ECPrivateKeyWithName ;
75
73
import static org .jruby .ext .openssl .impl .PKey .readECPrivateKey ;
74
+ import org .jruby .ext .openssl .util .ByteArrayOutputStream ;
75
+ import org .jruby .ext .openssl .x509store .PEMInputOutput ;
76
76
77
77
/**
78
78
* OpenSSL::PKey::EC implementation.
81
81
*/
82
82
public final class PKeyEC extends PKey {
83
83
84
- // TODO
85
- // private static final long serialVersionUID = -1L;
84
+ private static final long serialVersionUID = 1L ;
86
85
87
86
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator () {
88
87
public PKeyEC allocate (Ruby runtime , RubyClass klass ) { return new PKeyEC (runtime , klass ); }
@@ -166,6 +165,13 @@ private static ASN1ObjectIdentifier getCurveOID(final String curveName) {
166
165
throw new IllegalStateException ("could not identify curve name: " + curveName );
167
166
}
168
167
168
+ private static boolean isCurveName (final String curveName ) {
169
+ try {
170
+ return getCurveOID (curveName ) != null ;
171
+ }
172
+ catch (IllegalStateException ex ) { return false ; }
173
+ }
174
+
169
175
private static String getCurveName (final ASN1ObjectIdentifier oid ) {
170
176
String name ;
171
177
name = org .bouncycastle .asn1 .sec .SECNamedCurves .getName (oid );
@@ -233,6 +239,12 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
233
239
if ( args .length > 1 ) pass = args [1 ];
234
240
final char [] passwd = password (pass );
235
241
final RubyString str = readInitArg (context , arg );
242
+ final String strJava = str .toString ();
243
+
244
+ if ( isCurveName (strJava ) ) {
245
+ this .curveName = strJava ;
246
+ return this ;
247
+ }
236
248
237
249
Object key = null ;
238
250
final KeyFactory ecdsaFactory ;
@@ -249,7 +261,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
249
261
boolean noClassDef = false ;
250
262
if ( key == null && ! noClassDef ) { // PEM_read_bio_DSAPrivateKey
251
263
try {
252
- key = readPrivateKey (str , passwd );
264
+ key = readPrivateKey (strJava , passwd );
253
265
}
254
266
catch (NoClassDefFoundError e ) { noClassDef = true ; debugStackTrace (runtime , e ); }
255
267
catch (PEMInputOutput .PasswordRequiredException retry ) {
@@ -262,14 +274,14 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
262
274
}
263
275
if ( key == null && ! noClassDef ) {
264
276
try {
265
- key = PEMInputOutput .readECPublicKey (new StringReader (str . toString () ), passwd );
277
+ key = PEMInputOutput .readECPublicKey (new StringReader (strJava ), passwd );
266
278
}
267
279
catch (NoClassDefFoundError e ) { noClassDef = true ; debugStackTrace (runtime , e ); }
268
280
catch (Exception e ) { debugStackTrace (runtime , e ); }
269
281
}
270
282
if ( key == null && ! noClassDef ) {
271
283
try {
272
- key = PEMInputOutput .readECPubKey (new StringReader (str . toString () ));
284
+ key = PEMInputOutput .readECPubKey (new StringReader (strJava ));
273
285
}
274
286
catch (NoClassDefFoundError e ) { noClassDef = true ; debugStackTrace (runtime , e ); }
275
287
catch (Exception e ) { debugStackTrace (runtime , e ); }
@@ -377,19 +389,84 @@ public PKeyEC generate_key(final ThreadContext context) {
377
389
378
390
@ JRubyMethod (name = "dsa_sign_asn1" )
379
391
public IRubyObject dsa_sign_asn1 (final ThreadContext context , final IRubyObject data ) {
380
- // final ECDomainParameters params = getDomainParameters();
381
392
try {
382
- ECGenParameterSpec genSpec = new ECGenParameterSpec (getCurveName ());
383
- KeyPairGenerator gen = SecurityHelper .getKeyPairGenerator ("ECDSA" ); // "BC"
384
- gen .initialize (genSpec , new SecureRandom ());
385
- KeyPair pair = gen .generateKeyPair ();
386
- this .publicKey = (ECPublicKey ) pair .getPublic ();
387
- this .privateKey = pair .getPrivate ();
393
+ ECNamedCurveParameterSpec params = ECNamedCurveTable .getParameterSpec (getCurveName ());
394
+ ASN1ObjectIdentifier oid = getCurveOID (getCurveName ());
395
+ ECNamedDomainParameters domainParams = new ECNamedDomainParameters (oid ,
396
+ params .getCurve (), params .getG (), params .getN (), params .getH (), params .getSeed ()
397
+ );
398
+
399
+ final ECDSASigner signer = new ECDSASigner ();
400
+ final ECPrivateKey privKey = (ECPrivateKey ) this .privateKey ;
401
+ signer .init (true , new ECPrivateKeyParameters (privKey .getS (), domainParams ));
402
+
403
+ final byte [] message = data .convertToString ().getBytes ();
404
+ BigInteger [] signature = signer .generateSignature (message ); // [r, s]
405
+
406
+ // final byte[] r = signature[0].toByteArray();
407
+ // final byte[] s = signature[1].toByteArray();
408
+ // // ASN.1 encode as: 0x30 len 0x02 rlen (r) 0x02 slen (s)
409
+ // final int len = 1 + (1 + r.length) + 1 + (1 + s.length);
410
+ //
411
+ // final byte[] encoded = new byte[1 + 1 + len]; int i;
412
+ // encoded[0] = 0x30;
413
+ // encoded[1] = (byte) len;
414
+ // encoded[2] = 0x20;
415
+ // encoded[3] = (byte) r.length;
416
+ // System.arraycopy(r, 0, encoded, i = 4, r.length); i += r.length;
417
+ // encoded[i++] = 0x20;
418
+ // encoded[i++] = (byte) s.length;
419
+ // System.arraycopy(s, 0, encoded, i, s.length);
420
+
421
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream ();
422
+ ASN1OutputStream asn1 = new ASN1OutputStream (bytes );
423
+
424
+ ASN1EncodableVector v = new ASN1EncodableVector ();
425
+ v .add (new ASN1Integer (signature [0 ])); // r
426
+ v .add (new ASN1Integer (signature [1 ])); // s
427
+
428
+ asn1 .writeObject (new DLSequence (v ));
429
+
430
+ return StringHelper .newString (context .runtime , bytes .buffer (), bytes .size ());
431
+ }
432
+ catch (IOException ex ) {
433
+ throw newECError (context .runtime , ex .toString ());
434
+ }
435
+ catch (RuntimeException ex ) {
436
+ throw newECError (context .runtime , ex .toString ());
437
+ }
438
+ }
439
+
440
+
441
+ @ JRubyMethod (name = "dh_compute_key" )
442
+ public IRubyObject dh_compute_key (final ThreadContext context , final IRubyObject point ) {
443
+ try {
444
+ KeyAgreement agreement = SecurityHelper .getKeyAgreement ("ECDH" ); // "BC"
445
+ agreement .init (getPrivateKey ());
446
+ if ( point .isNil () ) {
447
+ agreement .doPhase (getPublicKey (), true );
448
+ }
449
+ else {
450
+ final ECPoint ecPoint = ((Point ) point ).asECPoint ();
451
+ final String name = getCurveName ();
452
+
453
+ KeyFactory keyFactory = KeyFactory .getInstance ("EC" ); // "BC"
454
+ ECParameterSpec spec = getParamSpec (name );
455
+ ECPublicKey ecPublicKey = (ECPublicKey ) keyFactory .generatePublic (new ECPublicKeySpec (ecPoint , spec ));
456
+ agreement .doPhase (ecPublicKey , true );
457
+ }
458
+ final byte [] secret = agreement .generateSecret ();
459
+ return StringHelper .newString (context .runtime , secret );
460
+ }
461
+ catch (NoSuchAlgorithmException ex ) {
462
+ throw newECError (context .runtime , ex .toString ());
463
+ }
464
+ catch (InvalidKeyException ex ) {
465
+ throw newECError (context .runtime , ex .toString ());
388
466
}
389
467
catch (GeneralSecurityException ex ) {
390
468
throw newECError (context .runtime , ex .toString ());
391
469
}
392
- return this ;
393
470
}
394
471
395
472
private Group getGroup (boolean required ) {
@@ -433,18 +510,25 @@ public IRubyObject set_public_key(final ThreadContext context, final IRubyObject
433
510
throw context .runtime .newTypeError (arg , _EC (context .runtime ).getClass ("Point" ));
434
511
}
435
512
final Point point = (Point ) arg ;
436
- ECNamedCurveParameterSpec spec = ECNamedCurveTable .getParameterSpec (getCurveName ());
437
- ECParameterSpec params = new ECNamedCurveSpec (spec .getName (), spec .getCurve (), spec .getG (), spec .getN (), spec .getH (), spec .getSeed ());
438
- ECPublicKeySpec pubKeySpec = new ECPublicKeySpec (point .asECPoint (), params );
513
+ ECPublicKeySpec keySpec = new ECPublicKeySpec (point .asECPoint (), getParamSpec ());
439
514
try {
440
- this .publicKey = (ECPublicKey ) SecurityHelper .getKeyFactory ("ECDSA" ).generatePublic (pubKeySpec );
515
+ this .publicKey = (ECPublicKey ) SecurityHelper .getKeyFactory ("ECDSA" ).generatePublic (keySpec );
441
516
return arg ;
442
517
}
443
518
catch (GeneralSecurityException ex ) {
444
519
throw newECError (context .runtime , ex .getMessage ());
445
520
}
446
521
}
447
522
523
+ private static ECParameterSpec getParamSpec (final String curveName ) {
524
+ ECNamedCurveParameterSpec spec = ECNamedCurveTable .getParameterSpec (curveName );
525
+ return new ECNamedCurveSpec (spec .getName (), spec .getCurve (), spec .getG (), spec .getN (), spec .getH (), spec .getSeed ());
526
+ }
527
+
528
+ private ECParameterSpec getParamSpec () {
529
+ return getParamSpec (getCurveName ());
530
+ }
531
+
448
532
/**
449
533
* @return OpenSSL::BN
450
534
*/
@@ -464,11 +548,9 @@ public IRubyObject set_private_key(final ThreadContext context, final IRubyObjec
464
548
else {
465
549
s = (BigInteger ) arg ;
466
550
}
467
- ECNamedCurveParameterSpec spec = ECNamedCurveTable .getParameterSpec (getCurveName ());
468
- ECParameterSpec params = new ECNamedCurveSpec (spec .getName (), spec .getCurve (), spec .getG (), spec .getN (), spec .getH (), spec .getSeed ());
469
- ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec (s , params );
551
+ ECPrivateKeySpec keySpec = new ECPrivateKeySpec (s , getParamSpec ());
470
552
try {
471
- this .privateKey = SecurityHelper .getKeyFactory ("ECDSA" ).generatePrivate (privKeySpec );
553
+ this .privateKey = SecurityHelper .getKeyFactory ("ECDSA" ).generatePrivate (keySpec );
472
554
return arg ;
473
555
}
474
556
catch (GeneralSecurityException ex ) {
@@ -510,14 +592,14 @@ private byte[] toDER() throws IOException {
510
592
}
511
593
512
594
@ Override
513
- @ JRubyMethod (name = { "to_pem" } , alias = "export" , rest = true )
595
+ @ JRubyMethod (name = "to_pem" , alias = "export" , rest = true )
514
596
public RubyString to_pem (final IRubyObject [] args ) {
515
597
Arity .checkArgumentCount (getRuntime (), args , 0 , 2 );
516
598
517
599
CipherSpec spec = null ; char [] passwd = null ;
518
600
if ( args .length > 0 ) {
519
601
spec = cipherSpec ( args [0 ] );
520
- if ( args .length > 1 ) passwd = password (args [1 ]);
602
+ if ( args .length > 1 ) passwd = password ( args [1 ] );
521
603
}
522
604
523
605
try {
@@ -641,7 +723,7 @@ public IRubyObject degree(final ThreadContext context) {
641
723
public IRubyObject generator (final ThreadContext context ) {
642
724
if ( paramSpec == null ) return context .nil ;
643
725
final ECPoint generator = paramSpec .getGenerator ();
644
- final int bitLength = paramSpec .getOrder ().bitLength ();
726
+ // final int bitLength = paramSpec.getOrder().bitLength();
645
727
return new Point (context .runtime , generator , this );
646
728
}
647
729
@@ -665,7 +747,26 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args)
665
747
}
666
748
}
667
749
668
- final EllipticCurve getCurve () { return paramSpec .getCurve (); }
750
+ final EllipticCurve getCurve () {
751
+ if (paramSpec == null ) {
752
+ paramSpec = getParamSpec (getCurveName ());
753
+ }
754
+ return paramSpec .getCurve ();
755
+ }
756
+
757
+ // @Override
758
+ // @JRubyMethod
759
+ // @SuppressWarnings("unchecked")
760
+ // public IRubyObject inspect() {
761
+ // final EllipticCurve curve = getCurve();
762
+ // final StringBuilder part = new StringBuilder();
763
+ // String cname = getMetaClass().getRealClass().getName();
764
+ // part.append("#<").append(cname).append(":0x");
765
+ // part.append(Integer.toHexString(System.identityHashCode(this)));
766
+ // // part.append(' ');
767
+ // part.append(" a:").append(curve.getA()).append(" b:").append(curve.getA());
768
+ // return RubyString.newString(getRuntime(), part.append('>'));
769
+ // }
669
770
670
771
}
671
772
@@ -708,6 +809,11 @@ public Point(Ruby runtime, RubyClass type) {
708
809
this .group = group ;
709
810
}
710
811
812
+ private static RaiseException newError (final Ruby runtime , final String message ) {
813
+ final RubyClass Error = _EC (runtime ).getClass ("Point" ).getClass ("Error" );
814
+ return Utils .newError (runtime , Error , message );
815
+ }
816
+
711
817
@ JRubyMethod (rest = true , visibility = Visibility .PRIVATE )
712
818
public IRubyObject initialize (final ThreadContext context , final IRubyObject [] args ) {
713
819
final Ruby runtime = context .runtime ;
@@ -726,7 +832,13 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
726
832
}
727
833
if ( argc == 2 ) { // (group, bn)
728
834
final byte [] encoded = ((BN ) args [1 ]).getValue ().abs ().toByteArray ();
729
- this .point = ECPointUtil .decodePoint (group .getCurve (), encoded );
835
+ try {
836
+ this .point = ECPointUtil .decodePoint (group .getCurve (), encoded );
837
+ }
838
+ catch (IllegalArgumentException ex ) {
839
+ // MRI: OpenSSL::PKey::EC::Point::Error: invalid encoding
840
+ throw newError (context .runtime , ex .getMessage ());
841
+ }
730
842
}
731
843
732
844
return this ;
@@ -762,7 +874,30 @@ private int bitLength() {
762
874
@ JRubyMethod
763
875
public BN to_bn (final ThreadContext context ) {
764
876
final byte [] encoded = encode (bitLength (), point );
765
- return BN .newBN (context .runtime , new BigInteger (encoded ));
877
+ return BN .newBN (context .runtime , new BigInteger (1 , encoded ));
878
+ }
879
+
880
+ private boolean isInfinity () {
881
+ return point == ECPoint .POINT_INFINITY ;
882
+ }
883
+
884
+ @ JRubyMethod (name = "infinity?" )
885
+ public RubyBoolean infinity_p () {
886
+ return getRuntime ().newBoolean ( isInfinity () );
887
+ }
888
+
889
+ @ JRubyMethod (name = "set_to_infinity!" )
890
+ public IRubyObject set_to_infinity_b () {
891
+ this .point = ECPoint .POINT_INFINITY ;
892
+ return this ;
893
+ }
894
+
895
+ @ Override
896
+ @ JRubyMethod
897
+ @ SuppressWarnings ("unchecked" )
898
+ public IRubyObject inspect () {
899
+ VariableEntry entry = new VariableEntry ( "group" , group == null ? (Object ) "nil" : group );
900
+ return ObjectSupport .inspect (this , (List ) Collections .singletonList (entry ));
766
901
}
767
902
768
903
}
0 commit comments