@@ -860,6 +860,8 @@ static RaiseException newError(final Ruby runtime, final String message) {
860860
861861 private PointConversion conversionForm = PointConversion .UNCOMPRESSED ;
862862
863+ private int asn1Flag = 1 ; // OPENSSL_EC_NAMED_CURVE
864+
863865 private String curveName ;
864866 private RubyString impl_curve_name ;
865867
@@ -881,11 +883,55 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
881883 IRubyObject arg = args [0 ];
882884
883885 if ( arg instanceof Group ) {
884- this .curveName = ((Group ) arg ).curveName ;
886+ final Group src = (Group ) arg ;
887+ this .curveName = src .curveName ;
888+ this .impl_curve_name = src .impl_curve_name ;
889+ this .paramSpec = src .paramSpec ;
890+ this .asn1Flag = src .asn1Flag ;
891+ this .conversionForm = src .conversionForm ;
885892 return this ;
886893 }
887894
888- this .impl_curve_name = arg .convertToString ();
895+ final RubyString strArg = arg .convertToString ();
896+ final byte [] bytes = strArg .getBytes ();
897+ // Detect DER input: OID tag (0x06) for named curve, SEQUENCE tag (0x30) for explicit params
898+ if (bytes .length > 0 && (bytes [0 ] == 0x06 || bytes [0 ] == 0x30 )) {
899+ try {
900+ final ASN1Primitive primitive = ASN1Primitive .fromByteArray (bytes );
901+ if (primitive instanceof ASN1ObjectIdentifier ) {
902+ // Named curve: DER-encoded OID -> look up curve name
903+ setCurveName (runtime , PKeyEC .getCurveName ((ASN1ObjectIdentifier ) primitive ));
904+ this .asn1Flag = 1 ; // NAMED_CURVE
905+ return this ;
906+ } else if (primitive instanceof ASN1Sequence ) {
907+ // Explicit parameters: X9.62 ECParameters SEQUENCE
908+ final X9ECParameters ecParams = X9ECParameters .getInstance (primitive );
909+ final EllipticCurve curve = EC5Util .convertCurve (ecParams .getCurve (), ecParams .getSeed ());
910+ this .paramSpec = new ECParameterSpec (curve ,
911+ EC5Util .convertPoint (ecParams .getG ()),
912+ ecParams .getN (), ecParams .getH ().intValue ());
913+ this .asn1Flag = 0 ; // explicit
914+ return this ;
915+ }
916+ } catch (IOException e ) {
917+ // fall through to treat as curve name string
918+ }
919+ }
920+
921+ this .impl_curve_name = strArg ;
922+ }
923+ return this ;
924+ }
925+
926+ @ JRubyMethod (name = "initialize_copy" , visibility = Visibility .PRIVATE )
927+ public IRubyObject initialize_copy (final IRubyObject original ) {
928+ if (original instanceof Group ) {
929+ final Group src = (Group ) original ;
930+ this .curveName = src .curveName ;
931+ this .impl_curve_name = src .impl_curve_name ;
932+ this .paramSpec = src .paramSpec ;
933+ this .asn1Flag = src .asn1Flag ;
934+ this .conversionForm = src .conversionForm ;
889935 }
890936 return this ;
891937 }
@@ -984,6 +1030,48 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args)
9841030 }
9851031 }
9861032
1033+ @ JRubyMethod (name = "asn1_flag" )
1034+ public IRubyObject asn1_flag (final ThreadContext context ) {
1035+ return context .runtime .newFixnum (asn1Flag );
1036+ }
1037+
1038+ @ JRubyMethod (name = "asn1_flag=" )
1039+ public IRubyObject set_asn1_flag (final ThreadContext context , final IRubyObject flag_v ) {
1040+ this .asn1Flag = (int ) RubyFixnum .num2long (flag_v );
1041+ return flag_v ;
1042+ }
1043+
1044+ /**
1045+ * Serializes the group as DER. For named curves (NAMED_CURVE flag set) this is the
1046+ * DER encoding of the curve OID. For explicit parameters it is the DER encoding of
1047+ * the X9.62 ECParameters SEQUENCE – matching OpenSSL's i2d_ECPKParameters().
1048+ */
1049+ @ JRubyMethod (name = "to_der" )
1050+ public RubyString to_der (final ThreadContext context ) {
1051+ final Ruby runtime = context .runtime ;
1052+ try {
1053+ final byte [] encoded ;
1054+ if ((asn1Flag & 1 ) != 0 ) { // NAMED_CURVE: encode as DER OID
1055+ final ASN1ObjectIdentifier oid = getCurveOID (getCurveName ())
1056+ .orElseThrow (() -> newError (runtime , "invalid curve name: " + getCurveName ()));
1057+ encoded = oid .getEncoded (ASN1Encoding .DER );
1058+ } else { // explicit parameters: encode as X9.62 ECParameters SEQUENCE
1059+ final ECParameterSpec ps = getParamSpec ();
1060+ final ECCurve bcCurve = EC5Util .convertCurve (ps .getCurve ());
1061+ final X9ECParameters ecParameters = new X9ECParameters (
1062+ bcCurve ,
1063+ new X9ECPoint (EC5Util .convertPoint (bcCurve , ps .getGenerator ()), false ),
1064+ ps .getOrder (),
1065+ BigInteger .valueOf (ps .getCofactor ()),
1066+ ps .getCurve ().getSeed ());
1067+ encoded = ecParameters .getEncoded (ASN1Encoding .DER );
1068+ }
1069+ return StringHelper .newString (runtime , encoded );
1070+ } catch (IOException e ) {
1071+ throw newError (runtime , e .getMessage ());
1072+ }
1073+ }
1074+
9871075 private ECParameterSpec getParamSpec () {
9881076 if (paramSpec == null ) {
9891077 paramSpec = PKeyEC .getParamSpec (getCurveName ());
0 commit comments