3131import java .io .IOException ;
3232import java .io .InputStream ;
3333import java .io .PrintStream ;
34- import java .lang .reflect .InvocationTargetException ;
35- import java .lang .reflect .Method ;
3634import java .math .BigInteger ;
35+ import java .nio .charset .StandardCharsets ;
3736import java .text .ParseException ;
3837import java .util .Date ;
3938import java .util .Enumeration ;
@@ -548,12 +547,12 @@ private static Map<ASN1ObjectIdentifier, String> getSymLookup(final Ruby runtime
548547 { "NUMERICSTRING" , org .bouncycastle .asn1 .DERNumericString .class , "NumericString" },
549548 { "PRINTABLESTRING" , org .bouncycastle .asn1 .DERPrintableString .class , "PrintableString" },
550549 { "T61STRING" , org .bouncycastle .asn1 .DERT61String .class , "T61String" },
551- { "VIDEOTEXSTRING" , null , "VideotexString" },
550+ { "VIDEOTEXSTRING" , org . bouncycastle . asn1 . DERVideotexString . class , "VideotexString" },
552551 { "IA5STRING" , org .bouncycastle .asn1 .DERIA5String .class , "IA5String" },
553552 { "UTCTIME" , org .bouncycastle .asn1 .DERUTCTime .class , "UTCTime" },
554553 { "GENERALIZEDTIME" , org .bouncycastle .asn1 .DERGeneralizedTime .class , "GeneralizedTime" },
555- { "GRAPHICSTRING" , null , "GraphicString" },
556- { "ISO64STRING" , null , "ISO64String" },
554+ { "GRAPHICSTRING" , org . bouncycastle . asn1 . DERGraphicString . class , "GraphicString" },
555+ { "ISO64STRING" , org . bouncycastle . asn1 . DERVisibleString . class , "ISO64String" },
557556 { "GENERALSTRING" , org .bouncycastle .asn1 .DERGeneralString .class , "GeneralString" },
558557 // OpenSSL::ASN1::UNIVERSALSTRING (28) :
559558 { "UNIVERSALSTRING" , org .bouncycastle .asn1 .DERUniversalString .class , "UniversalString" },
@@ -662,25 +661,6 @@ static Class<? extends ASN1Encodable> typeClassSafe(final int typeId) {
662661 return typeClass (typeId );
663662 }
664663
665- static ASN1Encodable typeInstance (Class <? extends ASN1Encodable > type , Object value )
666- throws NoSuchMethodException , IllegalAccessException , InvocationTargetException {
667- Method getInstance = null ;
668- try {
669- getInstance = type .getMethod ("getInstance" , Object .class );
670- }
671- catch (NoSuchMethodException e ) {
672- Class superType = type .getSuperclass ();
673- try {
674- if ( superType != Object .class ) {
675- getInstance = type .getSuperclass ().getMethod ("getInstance" , Object .class );
676- }
677- }
678- catch (NoSuchMethodException e2 ) { }
679- if ( getInstance == null ) throw e ;
680- }
681- return (ASN1Encodable ) getInstance .invoke (null , value );
682- }
683-
684664 public static void createASN1 (final Ruby runtime , final RubyModule OpenSSL , final RubyClass OpenSSLError ) {
685665 final RubyModule ASN1 = OpenSSL .defineModuleUnder ("ASN1" );
686666 ASN1 .defineClassUnder ("ASN1Error" , OpenSSLError , OpenSSLError .getAllocator ());
@@ -976,7 +956,22 @@ static IRubyObject decodeObject(final ThreadContext context,
976956 final ByteList bytes ;
977957 if ( obj instanceof ASN1UTF8String ) {
978958 if ( type == null ) type = "UTF8String" ;
979- bytes = new ByteList (((ASN1UTF8String ) obj ).getString ().getBytes ("UTF-8" ), false );
959+ bytes = new ByteList (((ASN1UTF8String ) obj ).getString ().getBytes (StandardCharsets .UTF_8 ), false );
960+ }
961+ else if ( obj instanceof ASN1UniversalString ) {
962+ if ( type == null ) type = "UniversalString" ;
963+ bytes = new ByteList (((ASN1UniversalString ) obj ).getOctets (), false );
964+ }
965+ else if ( obj instanceof ASN1BMPString ) {
966+ if ( type == null ) type = "BMPString" ;
967+ final String val = ((ASN1BMPString ) obj ).getString ();
968+ final byte [] valBytes = new byte [val .length () * 2 ];
969+ for (int i = 0 ; i < val .length (); i ++) {
970+ char c = val .charAt (i );
971+ valBytes [i * 2 ] = (byte ) ((c >> 8 ) & 0xff );
972+ valBytes [i * 2 + 1 ] = (byte ) (c & 0xff );
973+ }
974+ bytes = new ByteList (valBytes , false );
980975 }
981976 else {
982977 if ( type == null ) {
@@ -995,14 +990,16 @@ else if ( obj instanceof ASN1T61String ) {
995990 else if ( obj instanceof ASN1GeneralString ) {
996991 type = "GeneralString" ;
997992 }
998- else if ( obj instanceof ASN1UniversalString ) {
999- type = "UniversalString " ;
993+ else if ( obj instanceof ASN1VideotexString ) {
994+ type = "VideotexString " ;
1000995 }
1001- else if ( obj instanceof ASN1BMPString ) {
1002- type = "BMPString" ;
996+ else if ( obj instanceof ASN1VisibleString ) {
997+ type = "ISO64String" ;
998+ }
999+ else if ( obj instanceof ASN1GraphicString ) {
1000+ type = "GraphicString" ;
10031001 }
10041002 else {
1005- // NOTE "VideotexString", "GraphicString", "ISO64String" not-handled in BC !
10061003 throw new IllegalArgumentException ("could not handle ASN1 string type: " + obj + " (" + obj .getClass ().getName () + ")" );
10071004 }
10081005 }
@@ -1652,38 +1649,50 @@ ASN1Encodable toASN1(final ThreadContext context) {
16521649 return new DERUTF8String ( val .asString ().toString () );
16531650 }
16541651 if ( type == DERBMPString .class ) {
1655- return new DERBMPString ( val .asString ().toString () );
1652+ return new DERBMPString (new String ( toBMPChars ( val .asString ().getByteList ())) );
16561653 }
16571654 if ( type == DERUniversalString .class ) {
16581655 return new DERUniversalString ( val .asString ().getBytes () );
16591656 }
16601657
16611658 if ( type == DERGeneralString .class ) {
1662- return ASN1GeneralString . getInstance ( val .asString ().getBytes () );
1659+ return new DERGeneralString ( val .asString ().toString () );
16631660 }
16641661 if ( type == DERVisibleString .class ) {
1665- return ASN1VisibleString . getInstance ( val .asString ().getBytes () );
1662+ return new DERVisibleString ( val .asString ().toString () );
16661663 }
16671664 if ( type == DERNumericString .class ) {
1668- return ASN1NumericString . getInstance ( val .asString ().getBytes () );
1665+ return new DERNumericString ( val .asString ().toString () );
16691666 }
1670-
1671- if ( val instanceof RubyString ) {
1672- try {
1673- return typeInstance (type , ( (RubyString ) val ).getBytes ());
1674- }
1675- catch (Exception e ) { // TODO exception handling
1676- debugStackTrace (context .runtime , e );
1677- throw Utils .newError (context .runtime , context .runtime .getRuntimeError (), e );
1678- }
1667+ if ( type == DERPrintableString .class ) {
1668+ return new DERPrintableString ( val .asString ().toString () );
1669+ }
1670+ if ( type == DERT61String .class ) {
1671+ return new DERT61String ( val .asString ().toString () );
1672+ }
1673+ if ( type == DERVideotexString .class ) {
1674+ return new DERVideotexString ( val .asString ().getBytes () );
1675+ }
1676+ if ( type == DERGraphicString .class ) {
1677+ return new DERGraphicString ( val .asString ().getBytes () );
16791678 }
16801679
1681- // TODO throw an exception here too?
1682- if ( isDebug (context .runtime ) ) {
1680+ if (isDebug (context .runtime )) {
16831681 debug (this + " toASN1() could not handle class " + getMetaClass () + " and value " + val .inspect () + " (" + val .getClass ().getName () + ")" );
16841682 }
1685- warn (context , "WARNING: unimplemented method called: OpenSSL::ASN1Data#toASN1 (" + type + ")" );
1686- return null ;
1683+ throw context .runtime .newNotImplementedError ("unimplemented method called: OpenSSL::ASN1Data#toASN1 (" + type + ")" );
1684+ }
1685+
1686+ private static char [] toBMPChars (final ByteList string ) {
1687+ assert string .length () % 2 == 0 ;
1688+
1689+ final int len = string .length () / 2 ;
1690+ final char [] chars = new char [len ];
1691+ for (int i = 0 ; i < len ; i ++) {
1692+ int si = i * 2 ;
1693+ chars [i ] = (char )((string .get (si ) << 8 ) | (string .get (si + 1 ) & 0xff ));
1694+ }
1695+ return chars ;
16871696 }
16881697
16891698 private static BigInteger bigIntegerValue (final IRubyObject val ) {
0 commit comments