49
49
import org .bouncycastle .asn1 .ASN1Primitive ;
50
50
import org .bouncycastle .asn1 .ASN1String ;
51
51
import org .bouncycastle .asn1 .BERTags ;
52
+ import org .bouncycastle .asn1 .DERUTF8String ;
52
53
import org .bouncycastle .asn1 .DLSequence ;
53
54
import org .bouncycastle .asn1 .DLSet ;
54
55
import org .bouncycastle .asn1 .x500 .AttributeTypeAndValue ;
@@ -176,6 +177,7 @@ public X509Name(Ruby runtime, RubyClass type) {
176
177
private final List <RubyInteger > types ;
177
178
178
179
private transient X500Name name ;
180
+ private transient X500Name canonicalName ;
179
181
180
182
private void fromASN1Sequence (final byte [] encoded ) {
181
183
try {
@@ -242,6 +244,7 @@ private void addValue(final ASN1Encodable value) {
242
244
@ SuppressWarnings ("unchecked" )
243
245
private void addType (final Ruby runtime , final ASN1Encodable value ) {
244
246
this .name = null ; // NOTE: each fromX factory calls this ...
247
+ this .canonicalName = null ;
245
248
final Integer type = ASN1 .typeId (value );
246
249
if ( type == null ) {
247
250
warn (runtime .getCurrentContext (), this + " addType() could not resolve type for: " +
@@ -256,6 +259,7 @@ private void addType(final Ruby runtime, final ASN1Encodable value) {
256
259
private void addEntry (ASN1ObjectIdentifier oid , RubyString value , RubyInteger type )
257
260
throws IOException {
258
261
this .name = null ;
262
+ this .canonicalName = null ;
259
263
this .oids .add (oid );
260
264
final ASN1Encodable convertedValue = getNameEntryConverted ().
261
265
getConvertedValue (oid , value .toString ()
@@ -515,6 +519,50 @@ final X500Name getX500Name() {
515
519
return name = builder .build ();
516
520
}
517
521
522
+ final X500Name getCanonicalX500Name () {
523
+ if ( canonicalName != null ) return canonicalName ;
524
+
525
+ final X500NameBuilder builder = new X500NameBuilder ( BCStyle .INSTANCE );
526
+ for ( int i = 0 ; i < oids .size (); i ++ ) {
527
+ ASN1Encodable value = values .get (i );
528
+ value = canonicalize (value );
529
+ builder .addRDN ( oids .get (i ), value );
530
+ }
531
+ return canonicalName = builder .build ();
532
+ }
533
+
534
+ private ASN1Encodable canonicalize (ASN1Encodable value ) {
535
+ if (value instanceof ASN1String ) {
536
+ ASN1String string = (ASN1String ) value ;
537
+ return new DERUTF8String (canonicalize (string .getString ()));
538
+ }
539
+ return value ;
540
+ }
541
+
542
+ private String canonicalize (String string ) {
543
+ //asn1_string_canon (trim, to lower case, collapse multiple spaces)
544
+ string = string .trim ();
545
+ if (string .length () == 0 ) {
546
+ return string ;
547
+ }
548
+
549
+ StringBuilder out = new StringBuilder ();
550
+ int i = 0 ;
551
+ while (i < string .length ()) {
552
+ char c = string .charAt (i );
553
+ if (Character .isWhitespace (c )){
554
+ out .append (' ' );
555
+ while (i < string .length () && Character .isWhitespace (string .charAt (i ))) {
556
+ i ++;
557
+ }
558
+ } else {
559
+ out .append (Character .toLowerCase (c ));
560
+ i ++;
561
+ }
562
+ }
563
+ return out .toString ();
564
+ }
565
+
518
566
@ JRubyMethod (name = { "cmp" , "<=>" })
519
567
public RubyFixnum cmp (IRubyObject other ) {
520
568
if ( equals (other ) ) {
@@ -523,8 +571,8 @@ public RubyFixnum cmp(IRubyObject other) {
523
571
// TODO: do we really need cmp - if so what order huh?
524
572
if ( other instanceof X509Name ) {
525
573
final X509Name that = (X509Name ) other ;
526
- final X500Name thisName = this .getX500Name ();
527
- final X500Name thatName = that .getX500Name ();
574
+ final X500Name thisName = this .getCanonicalX500Name ();
575
+ final X500Name thatName = that .getCanonicalX500Name ();
528
576
int cmp = thisName .toString ().compareTo ( thatName .toString () );
529
577
return RubyFixnum .newFixnum ( getRuntime (), cmp );
530
578
}
@@ -536,8 +584,8 @@ public boolean equals(Object other) {
536
584
if ( this == other ) return true ;
537
585
if ( other instanceof X509Name ) {
538
586
final X509Name that = (X509Name ) other ;
539
- final X500Name thisName = this .getX500Name ();
540
- final X500Name thatName = that .getX500Name ();
587
+ final X500Name thisName = this .getCanonicalX500Name ();
588
+ final X500Name thatName = that .getCanonicalX500Name ();
541
589
return thisName .equals (thatName );
542
590
}
543
591
return false ;
@@ -546,15 +594,14 @@ public boolean equals(Object other) {
546
594
@ Override
547
595
public int hashCode () {
548
596
try {
549
- return Name .hash ( getX500Name () );
597
+ return ( int ) Name .hash ( getCanonicalX500Name () );
550
598
}
551
599
catch (IOException e ) {
552
600
debugStackTrace (getRuntime (), e ); return 0 ;
553
601
}
554
602
catch (RuntimeException e ) {
555
603
debugStackTrace (getRuntime (), e ); return 0 ;
556
604
}
557
- // return 41 * this.oids.hashCode();
558
605
}
559
606
560
607
@ JRubyMethod (name = "eql?" )
@@ -571,7 +618,32 @@ public IRubyObject eql_p(final IRubyObject obj) {
571
618
@ Override
572
619
@ JRubyMethod
573
620
public RubyFixnum hash () {
574
- return getRuntime ().newFixnum ( hashCode () );
621
+ long hash ;
622
+ try {
623
+ hash = Name .hash ( getCanonicalX500Name () );
624
+ }
625
+ catch (IOException e ) {
626
+ debugStackTrace (getRuntime (), e ); hash = 0 ;
627
+ }
628
+ catch (RuntimeException e ) {
629
+ debugStackTrace (getRuntime (), e ); hash = 0 ;
630
+ }
631
+ return getRuntime ().newFixnum (hash );
632
+ }
633
+
634
+ @ JRubyMethod
635
+ public RubyFixnum hash_old () {
636
+ int hash ;
637
+ try {
638
+ hash = Name .hashOld ( getX500Name () );
639
+ }
640
+ catch (IOException e ) {
641
+ debugStackTrace (getRuntime (), e ); hash = 0 ;
642
+ }
643
+ catch (RuntimeException e ) {
644
+ debugStackTrace (getRuntime (), e ); hash = 0 ;
645
+ }
646
+ return getRuntime ().newFixnum ( hash );
575
647
}
576
648
577
649
@ JRubyMethod
0 commit comments