37
37
import java .util .Locale ;
38
38
import java .util .Optional ;
39
39
import javax .crypto .KeyAgreement ;
40
+
40
41
import org .bouncycastle .asn1 .ASN1EncodableVector ;
41
42
import org .bouncycastle .asn1 .ASN1Encoding ;
42
43
import org .bouncycastle .asn1 .ASN1InputStream ;
45
46
import org .bouncycastle .asn1 .ASN1OutputStream ;
46
47
import org .bouncycastle .asn1 .ASN1Primitive ;
47
48
import org .bouncycastle .asn1 .ASN1Sequence ;
49
+ import org .bouncycastle .asn1 .DERNull ;
48
50
import org .bouncycastle .asn1 .DERSequence ;
49
-
51
+ import org .bouncycastle .asn1 .pkcs .PrivateKeyInfo ;
52
+ import org .bouncycastle .asn1 .x509 .AlgorithmIdentifier ;
53
+ import org .bouncycastle .asn1 .x509 .SubjectPublicKeyInfo ;
54
+ import org .bouncycastle .asn1 .x9 .X962Parameters ;
55
+ import org .bouncycastle .asn1 .x9 .X9ECParameters ;
56
+ import org .bouncycastle .asn1 .x9 .X9ECPoint ;
57
+ import org .bouncycastle .asn1 .x9 .X9ObjectIdentifiers ;
50
58
import org .bouncycastle .crypto .params .ECDomainParameters ;
51
59
import org .bouncycastle .crypto .params .ECPrivateKeyParameters ;
52
60
import org .bouncycastle .crypto .params .ECPublicKeyParameters ;
53
61
import org .bouncycastle .crypto .signers .ECDSASigner ;
54
62
import org .bouncycastle .jcajce .provider .asymmetric .util .EC5Util ;
55
63
import org .bouncycastle .jcajce .provider .asymmetric .util .ECUtil ;
64
+ import org .bouncycastle .jcajce .provider .config .ProviderConfiguration ;
56
65
import org .bouncycastle .jce .ECNamedCurveTable ;
57
66
import org .bouncycastle .jce .ECPointUtil ;
67
+ import org .bouncycastle .jce .provider .BouncyCastleProvider ;
58
68
import org .bouncycastle .jce .spec .ECNamedCurveParameterSpec ;
59
69
import org .bouncycastle .jce .spec .ECNamedCurveSpec ;
60
-
61
70
import org .bouncycastle .math .ec .ECAlgorithms ;
62
71
import org .bouncycastle .math .ec .ECCurve ;
63
72
import org .jruby .Ruby ;
82
91
import org .jruby .runtime .component .VariableEntry ;
83
92
84
93
import org .jruby .ext .openssl .impl .CipherSpec ;
85
- import static org .jruby .ext .openssl .OpenSSL .debug ;
86
- import static org .jruby .ext .openssl .OpenSSL .debugStackTrace ;
87
94
import org .jruby .ext .openssl .impl .ECPrivateKeyWithName ;
88
- import static org . jruby . ext . openssl . impl . PKey . readECPrivateKey ;
95
+
89
96
import org .jruby .ext .openssl .util .ByteArrayOutputStream ;
90
97
import org .jruby .ext .openssl .x509store .PEMInputOutput ;
91
98
99
+ import static org .jruby .ext .openssl .OpenSSL .debug ;
100
+ import static org .jruby .ext .openssl .OpenSSL .debugStackTrace ;
101
+ import static org .jruby .ext .openssl .impl .PKey .readECPrivateKey ;
102
+
92
103
/**
93
104
* OpenSSL::PKey::EC implementation.
94
105
*
@@ -626,27 +637,111 @@ public RubyBoolean private_p() {
626
637
return privateKey != null ? getRuntime ().getTrue () : getRuntime ().getFalse ();
627
638
}
628
639
640
+ @ JRubyMethod
641
+ public RubyString public_to_der (ThreadContext context ) {
642
+ return public_to_der (context .runtime );
643
+ }
644
+
645
+ private RubyString public_to_der (final Ruby runtime ) {
646
+ final byte [] bytes ;
647
+ try {
648
+ bytes = publicKey .getEncoded ();
649
+ } catch (Exception e ) {
650
+ throw newECError (runtime , e .getMessage (), e );
651
+ }
652
+ return StringHelper .newString (runtime , bytes );
653
+ }
654
+
629
655
@ Override
630
656
@ JRubyMethod (name = "to_der" )
631
657
public RubyString to_der () {
632
- final byte [] bytes ;
658
+ final Ruby runtime = getRuntime ();
659
+ if (publicKey != null && privateKey == null ) {
660
+ return public_to_der (runtime );
661
+ }
662
+ if (privateKey == null ) {
663
+ throw new IllegalStateException ("private key as well as public key are null" );
664
+ }
665
+
633
666
try {
634
- bytes = toDER ();
667
+ byte [] encoded = toPrivateKeyStructure ((ECPrivateKey ) privateKey , publicKey , false ).getEncoded (ASN1Encoding .DER );
668
+ return StringHelper .newString (runtime , encoded );
669
+ } catch (Exception e ) {
670
+ throw newECError (runtime , e .getMessage (), e );
671
+ }
672
+ }
673
+
674
+ @ JRubyMethod
675
+ public RubyString private_to_der (ThreadContext context ) {
676
+ return private_to_der (context .runtime );
677
+ }
678
+
679
+ private RubyString private_to_der (final Ruby runtime ) {
680
+ final byte [] encoded ;
681
+ if (privateKey instanceof ECPrivateKey ) {
682
+ try {
683
+ encoded = toPrivateKeyInfo ((ECPrivateKey ) privateKey , publicKey ).getEncoded (ASN1Encoding .DER );
684
+ } catch (IOException e ) {
685
+ throw newECError (runtime , e .getMessage (), e );
686
+ }
687
+ } else {
688
+ try {
689
+ encoded = privateKey .getEncoded ();
690
+ } catch (Exception e ) {
691
+ throw newECError (runtime , e .getMessage (), e );
692
+ }
635
693
}
636
- catch (IOException e ) {
637
- throw newECError (getRuntime (), e .getMessage ());
694
+ return StringHelper .newString (runtime , encoded );
695
+ }
696
+
697
+ private static org .bouncycastle .asn1 .sec .ECPrivateKey toPrivateKeyStructure (final ECPrivateKey privateKey ,
698
+ final ECPublicKey publicKey ,
699
+ final boolean compressed ) throws IOException {
700
+ final ProviderConfiguration configuration = BouncyCastleProvider .CONFIGURATION ;
701
+ final ECParameterSpec ecSpec = privateKey .getParams ();
702
+ final X962Parameters params = getDomainParametersFromName (ecSpec , compressed );
703
+
704
+ int orderBitLength = ECUtil .getOrderBitLength (configuration , ecSpec == null ? null : ecSpec .getOrder (), privateKey .getS ());
705
+
706
+ if (publicKey == null ) {
707
+ return new org .bouncycastle .asn1 .sec .ECPrivateKey (orderBitLength , privateKey .getS (), params );
638
708
}
639
- return StringHelper .newString (getRuntime (), bytes );
709
+
710
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfo .getInstance (ASN1Primitive .fromByteArray (publicKey .getEncoded ()));
711
+ return new org .bouncycastle .asn1 .sec .ECPrivateKey (orderBitLength , privateKey .getS (), info .getPublicKeyData (), params );
712
+ }
713
+
714
+ private static PrivateKeyInfo toPrivateKeyInfo (final ECPrivateKey privateKey ,
715
+ final ECPublicKey publicKey ) throws IOException {
716
+ final ECParameterSpec ecSpec = privateKey .getParams ();
717
+ final X962Parameters params = getDomainParametersFromName (ecSpec , false );
718
+
719
+ org .bouncycastle .asn1 .sec .ECPrivateKey keyStructure = toPrivateKeyStructure (privateKey , publicKey , false );
720
+ return new PrivateKeyInfo (new AlgorithmIdentifier (X9ObjectIdentifiers .id_ecPublicKey , params ), keyStructure );
640
721
}
641
722
642
- private byte [] toDER () throws IOException {
643
- if ( publicKey != null && privateKey == null ) {
644
- return publicKey .getEncoded ();
723
+ private static X962Parameters getDomainParametersFromName (ECParameterSpec ecSpec , boolean compressed ) {
724
+ if (ecSpec instanceof ECNamedCurveSpec ) {
725
+ ASN1ObjectIdentifier curveOid = ECUtil .getNamedCurveOid (((ECNamedCurveSpec )ecSpec ).getName ());
726
+ if (curveOid == null )
727
+ {
728
+ curveOid = new ASN1ObjectIdentifier (((ECNamedCurveSpec )ecSpec ).getName ());
729
+ }
730
+ return new X962Parameters (curveOid );
645
731
}
646
- if ( privateKey == null ) {
647
- throw new IllegalStateException ( "private key as well as public key are null" );
732
+ if (ecSpec == null ) {
733
+ return new X962Parameters ( DERNull . INSTANCE );
648
734
}
649
- return privateKey .getEncoded ();
735
+ ECCurve curve = EC5Util .convertCurve (ecSpec .getCurve ());
736
+
737
+ X9ECParameters ecParameters = new X9ECParameters (
738
+ curve ,
739
+ new X9ECPoint (EC5Util .convertPoint (curve , ecSpec .getGenerator ()), compressed ),
740
+ ecSpec .getOrder (),
741
+ BigInteger .valueOf (ecSpec .getCofactor ()),
742
+ ecSpec .getCurve ().getSeed ());
743
+
744
+ return new X962Parameters (ecParameters );
650
745
}
651
746
652
747
@ Override
@@ -660,17 +755,26 @@ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
660
755
if ( args .length > 1 ) passwd = password (context , args [1 ], null );
661
756
}
662
757
758
+ if (privateKey == null ) {
759
+ return public_to_pem (context );
760
+ }
761
+
663
762
try {
664
763
final StringWriter writer = new StringWriter ();
665
- if ( privateKey != null ) {
666
- PEMInputOutput .writeECPrivateKey (writer , (ECPrivateKey ) privateKey , spec , passwd );
667
- }
668
- else {
669
- PEMInputOutput .writeECPublicKey (writer , publicKey );
670
- }
764
+ PEMInputOutput .writeECPrivateKey (writer , (ECPrivateKey ) privateKey , spec , passwd );
671
765
return RubyString .newString (context .runtime , writer .getBuffer ());
766
+ } catch (IOException ex ) {
767
+ throw newECError (context .runtime , ex .getMessage ());
672
768
}
673
- catch (IOException ex ) {
769
+ }
770
+
771
+ @ JRubyMethod
772
+ public RubyString public_to_pem (ThreadContext context ) {
773
+ try {
774
+ final StringWriter writer = new StringWriter ();
775
+ PEMInputOutput .writeECPublicKey (writer , publicKey );
776
+ return RubyString .newString (context .runtime , writer .getBuffer ());
777
+ } catch (IOException ex ) {
674
778
throw newECError (context .runtime , ex .getMessage ());
675
779
}
676
780
}
0 commit comments