3737import java .util .Locale ;
3838import java .util .Optional ;
3939import javax .crypto .KeyAgreement ;
40+
4041import org .bouncycastle .asn1 .ASN1EncodableVector ;
4142import org .bouncycastle .asn1 .ASN1Encoding ;
4243import org .bouncycastle .asn1 .ASN1InputStream ;
4546import org .bouncycastle .asn1 .ASN1OutputStream ;
4647import org .bouncycastle .asn1 .ASN1Primitive ;
4748import org .bouncycastle .asn1 .ASN1Sequence ;
49+ import org .bouncycastle .asn1 .DERNull ;
4850import 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 ;
5058import org .bouncycastle .crypto .params .ECDomainParameters ;
5159import org .bouncycastle .crypto .params .ECPrivateKeyParameters ;
5260import org .bouncycastle .crypto .params .ECPublicKeyParameters ;
5361import org .bouncycastle .crypto .signers .ECDSASigner ;
5462import org .bouncycastle .jcajce .provider .asymmetric .util .EC5Util ;
5563import org .bouncycastle .jcajce .provider .asymmetric .util .ECUtil ;
64+ import org .bouncycastle .jcajce .provider .config .ProviderConfiguration ;
5665import org .bouncycastle .jce .ECNamedCurveTable ;
5766import org .bouncycastle .jce .ECPointUtil ;
67+ import org .bouncycastle .jce .provider .BouncyCastleProvider ;
5868import org .bouncycastle .jce .spec .ECNamedCurveParameterSpec ;
5969import org .bouncycastle .jce .spec .ECNamedCurveSpec ;
60-
6170import org .bouncycastle .math .ec .ECAlgorithms ;
6271import org .bouncycastle .math .ec .ECCurve ;
6372import org .jruby .Ruby ;
8291import org .jruby .runtime .component .VariableEntry ;
8392
8493import org .jruby .ext .openssl .impl .CipherSpec ;
85- import static org .jruby .ext .openssl .OpenSSL .debug ;
86- import static org .jruby .ext .openssl .OpenSSL .debugStackTrace ;
8794import org .jruby .ext .openssl .impl .ECPrivateKeyWithName ;
88- import static org . jruby . ext . openssl . impl . PKey . readECPrivateKey ;
95+
8996import org .jruby .ext .openssl .util .ByteArrayOutputStream ;
9097import org .jruby .ext .openssl .x509store .PEMInputOutput ;
9198
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+
92103/**
93104 * OpenSSL::PKey::EC implementation.
94105 *
@@ -626,27 +637,111 @@ public RubyBoolean private_p() {
626637 return privateKey != null ? getRuntime ().getTrue () : getRuntime ().getFalse ();
627638 }
628639
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+
629655 @ Override
630656 @ JRubyMethod (name = "to_der" )
631657 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+
633666 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+ }
635693 }
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 );
638708 }
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 );
640721 }
641722
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 );
645731 }
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 );
648734 }
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 );
650745 }
651746
652747 @ Override
@@ -660,17 +755,26 @@ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
660755 if ( args .length > 1 ) passwd = password (context , args [1 ], null );
661756 }
662757
758+ if (privateKey == null ) {
759+ return public_to_pem (context );
760+ }
761+
663762 try {
664763 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 );
671765 return RubyString .newString (context .runtime , writer .getBuffer ());
766+ } catch (IOException ex ) {
767+ throw newECError (context .runtime , ex .getMessage ());
672768 }
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 ) {
674778 throw newECError (context .runtime , ex .getMessage ());
675779 }
676780 }
0 commit comments