44
44
import java .security .cert .CRLException ;
45
45
import java .security .cert .CertificateFactory ;
46
46
import java .security .cert .X509CRLEntry ;
47
+ import java .security .interfaces .DSAParams ;
48
+ import java .security .interfaces .DSAPublicKey ;
49
+ import java .security .interfaces .RSAPublicKey ;
47
50
import java .util .Arrays ;
51
+ import java .util .Collection ;
48
52
import java .util .Comparator ;
49
53
50
54
import org .bouncycastle .asn1 .ASN1Encodable ;
56
60
import org .bouncycastle .asn1 .DERSequence ;
57
61
import org .bouncycastle .asn1 .DLSequence ;
58
62
import org .bouncycastle .asn1 .x500 .X500Name ;
63
+ import org .bouncycastle .asn1 .x509 .AlgorithmIdentifier ;
64
+ import org .bouncycastle .asn1 .x509 .CertificateList ;
65
+ import org .bouncycastle .asn1 .x509 .Extension ;
59
66
import org .bouncycastle .asn1 .x509 .Extensions ;
67
+ import org .bouncycastle .cert .CertException ;
60
68
import org .bouncycastle .cert .X509CRLHolder ;
61
69
import org .bouncycastle .cert .X509v2CRLBuilder ;
70
+ import org .bouncycastle .crypto .params .AsymmetricKeyParameter ;
71
+ import org .bouncycastle .crypto .params .DSAParameters ;
72
+ import org .bouncycastle .crypto .params .DSAPublicKeyParameters ;
73
+ import org .bouncycastle .crypto .params .RSAKeyParameters ;
62
74
import org .bouncycastle .jce .provider .X509CRLObject ;
63
75
import org .bouncycastle .operator .ContentSigner ;
76
+ import org .bouncycastle .operator .ContentVerifier ;
77
+ import org .bouncycastle .operator .ContentVerifierProvider ;
78
+ import org .bouncycastle .operator .DefaultDigestAlgorithmIdentifierFinder ;
79
+ import org .bouncycastle .operator .DigestAlgorithmIdentifierFinder ;
80
+ import org .bouncycastle .operator .OperatorException ;
81
+ import org .bouncycastle .operator .bc .BcDSAContentVerifierProviderBuilder ;
82
+ import org .bouncycastle .operator .bc .BcRSAContentVerifierProviderBuilder ;
64
83
import org .bouncycastle .operator .jcajce .JcaContentSignerBuilder ;
65
84
66
85
import org .joda .time .DateTime ;
87
106
88
107
import static org .jruby .ext .openssl .OpenSSL .*;
89
108
import static org .jruby .ext .openssl .X509 ._X509 ;
90
- import static org .jruby .ext .openssl .X509Extension ._Extension ;
91
109
import static org .jruby .ext .openssl .X509Extension .newExtension ;
92
110
import static org .jruby .ext .openssl .StringHelper .appendGMTDateTime ;
93
111
import static org .jruby .ext .openssl .StringHelper .appendLowerHexValue ;
@@ -122,7 +140,7 @@ public static void createX509CRL(final Ruby runtime, final RubyModule _X509) {
122
140
123
141
private boolean changed = true ;
124
142
125
- private java .security .cert .X509CRL crl ;
143
+ private java .security .cert .X509CRL crl = null ;
126
144
private transient X509CRLHolder crlHolder ;
127
145
private transient ASN1Primitive crlValue ;
128
146
@@ -150,12 +168,16 @@ java.security.cert.X509CRL getCRL() {
150
168
catch (IOException ex ) {
151
169
throw newCRLError (getRuntime (), ex );
152
170
}
171
+ catch (GeneralSecurityException ex ) {
172
+ throw newCRLError (getRuntime (), ex );
173
+ }
153
174
}
154
175
155
- X509CRLHolder getCRLHolder () {
176
+ private X509CRLHolder getCRLHolder (boolean allowNull ) {
156
177
if ( crlHolder != null ) return crlHolder ;
157
178
try {
158
179
if ( crl == null ) {
180
+ if ( allowNull ) return null ;
159
181
throw new IllegalStateException ("no crl" );
160
182
}
161
183
return crlHolder = new X509CRLHolder (crl .getEncoded ());
@@ -177,17 +199,20 @@ private byte[] getSignature() {
177
199
return getCRL ().getSignature ();
178
200
}
179
201
180
- private java .security .cert .X509CRL generateCRL (final byte [] bytes ,
181
- final int offset , final int length ) {
182
- try {
183
- CertificateFactory factory = SecurityHelper .getCertificateFactory ("X.509" );
184
- return (java .security .cert .X509CRL ) factory .generateCRL (
185
- new ByteArrayInputStream (bytes , offset , length )
186
- );
187
- }
188
- catch (GeneralSecurityException e ) {
189
- throw newCRLError (getRuntime (), e .getMessage ());
190
- }
202
+ private static boolean avoidJavaSecurity = false ;
203
+
204
+ private static java .security .cert .X509CRL generateCRL (
205
+ final byte [] bytes , final int offset , final int length )
206
+ throws GeneralSecurityException {
207
+ CertificateFactory factory = SecurityHelper .getCertificateFactory ("X.509" );
208
+ return (java .security .cert .X509CRL ) factory .generateCRL (
209
+ new ByteArrayInputStream (bytes , offset , length )
210
+ );
211
+ }
212
+
213
+ private static X509CRLHolder parseCRLHolder (
214
+ final byte [] bytes , final int offset , final int length ) throws IOException {
215
+ return new X509CRLHolder (new ByteArrayInputStream (bytes , offset , length ));
191
216
}
192
217
193
218
@ JRubyMethod (name = "initialize" , rest = true , visibility = Visibility .PRIVATE )
@@ -199,9 +224,25 @@ public IRubyObject initialize(final ThreadContext context,
199
224
200
225
if ( Arity .checkArgumentCount (runtime , args , 0 , 1 ) == 0 ) return this ;
201
226
202
- final ByteList bytes = args [0 ].asString ().getByteList ();
203
- final int offset = bytes .getBegin (); final int length = bytes .getRealSize ();
204
- this .crl = generateCRL (bytes .unsafeBytes (), offset , length );
227
+ final ByteList strList = args [0 ].asString ().getByteList ();
228
+ final byte [] bytes = strList .unsafeBytes ();
229
+ final int offset = strList .getBegin (); final int length = strList .getRealSize ();
230
+ try {
231
+ if ( avoidJavaSecurity ) {
232
+ this .crlHolder = parseCRLHolder (bytes , offset , length );
233
+ }
234
+ else {
235
+ this .crl = generateCRL (bytes , offset , length );
236
+ }
237
+ }
238
+ catch (IOException e ) {
239
+ debugStackTrace (runtime , e );
240
+ throw newCRLError (runtime , e );
241
+ }
242
+ catch (GeneralSecurityException e ) {
243
+ debugStackTrace (runtime , e );
244
+ throw newCRLError (runtime , e );
245
+ }
205
246
206
247
set_last_update ( context , RubyTime .newTime (runtime , crl .getThisUpdate ().getTime ()) );
207
248
set_next_update ( context , RubyTime .newTime (runtime , crl .getNextUpdate ().getTime ()) );
@@ -210,22 +251,7 @@ public IRubyObject initialize(final ThreadContext context,
210
251
final int version = crl .getVersion ();
211
252
this .version = runtime .newFixnum ( version > 0 ? version - 1 : 2 );
212
253
213
-
214
- final RubyClass _Extension = _Extension (runtime );
215
-
216
- final Set <String > criticalExtOIDs = crl .getCriticalExtensionOIDs ();
217
- if ( criticalExtOIDs != null ) {
218
- for ( final String extOID : criticalExtOIDs ) {
219
- addExtension (context , _Extension , extOID , true );
220
- }
221
- }
222
-
223
- final Set <String > nonCriticalExtOIDs = crl .getNonCriticalExtensionOIDs ();
224
- if ( nonCriticalExtOIDs != null ) {
225
- for ( final String extOID : nonCriticalExtOIDs ) {
226
- addExtension (context , _Extension , extOID , false );
227
- }
228
- }
254
+ extractExtensions (context );
229
255
230
256
Set <? extends X509CRLEntry > revokedCRLs = crl .getRevokedCertificates ();
231
257
if ( revokedCRLs != null && ! revokedCRLs .isEmpty () ) {
@@ -245,8 +271,48 @@ public int compare(X509CRLEntry o1, X509CRLEntry o2) {
245
271
return this ;
246
272
}
247
273
274
+ private void extractExtensions (final ThreadContext context ) {
275
+ if ( crlHolder != null ) extractExtensions (context , crlHolder );
276
+ else extractExtensionsCRL (context , getCRL ());
277
+ }
278
+
279
+ @ SuppressWarnings ("unchecked" )
280
+ private void extractExtensions (final ThreadContext context , final X509CRLHolder crl ) {
281
+ if ( ! crlHolder .hasExtensions () ) return ;
282
+ for ( ASN1ObjectIdentifier oid : (Collection <ASN1ObjectIdentifier >) crl .getExtensionOIDs () ) {
283
+ addExtension (context , oid , crl );
284
+ }
285
+ }
286
+
248
287
private void addExtension (final ThreadContext context ,
249
- final RubyClass _Extension , final String extOID , final boolean critical ) {
288
+ final ASN1ObjectIdentifier extOID , final X509CRLHolder crl ) {
289
+ final Extension ext = crl .getExtension (extOID );
290
+ final IRubyObject extension = newExtension (context .runtime , extOID , ext );
291
+ this .extensions .append (extension );
292
+ }
293
+
294
+ private void extractExtensionsCRL (final ThreadContext context ,
295
+ final java .security .cert .X509Extension crl ) {
296
+ //final RubyClass _Extension = _Extension(context.runtime);
297
+
298
+ final Set <String > criticalExtOIDs = crl .getCriticalExtensionOIDs ();
299
+ if ( criticalExtOIDs != null ) {
300
+ for ( final String extOID : criticalExtOIDs ) {
301
+ addExtensionCRL (context , extOID , crl , true );
302
+ }
303
+ }
304
+
305
+ final Set <String > nonCriticalExtOIDs = crl .getNonCriticalExtensionOIDs ();
306
+ if ( nonCriticalExtOIDs != null ) {
307
+ for ( final String extOID : nonCriticalExtOIDs ) {
308
+ addExtensionCRL (context , extOID , crl , false );
309
+ }
310
+ }
311
+ }
312
+
313
+ private void addExtensionCRL (final ThreadContext context ,
314
+ final String extOID , final java .security .cert .X509Extension crl ,
315
+ final boolean critical ) {
250
316
try {
251
317
final IRubyObject extension = newExtension (context , extOID , crl , critical );
252
318
if ( extension != null ) this .extensions .append (extension );
@@ -394,20 +460,24 @@ private RubyString signature_algorithm(final Ruby runtime) {
394
460
}
395
461
396
462
private String getSignatureAlgorithm (final Ruby runtime , final String def ) {
397
- if ( getCRL () == null ) return def ;
463
+ final X509CRLHolder crlHolder = getCRLHolder (true );
464
+ if ( crlHolder == null ) return def ;
398
465
466
+ ASN1ObjectIdentifier algId =
467
+ crlHolder .toASN1Structure ().getSignatureAlgorithm ().getAlgorithm ();
468
+ //ASN1ObjectIdentifier algId = ASN1.toObjectID( getCRL().getSigAlgOID(), true );
399
469
String algName ;
400
- ASN1ObjectIdentifier algId = ASN1 .toObjectID ( getCRL ().getSigAlgOID (), true );
401
470
if ( algId != null ) {
402
471
algName = ASN1 .o2a (runtime , algId , true );
403
472
}
404
- else {
405
- algName = getCRL ().getSigAlgName ();
406
- algId = ASN1 .toObjectID ( algName , true );
407
- if ( algId != null ) {
408
- algName = ASN1 .o2a (runtime , algId , true );
409
- }
410
- }
473
+ else algName = null ;
474
+ //else {
475
+ // algName = getCRL().getSigAlgName();
476
+ // algId = ASN1.toObjectID( algName, true );
477
+ // if ( algId != null ) {
478
+ // algName = ASN1.o2a(runtime, algId, true);
479
+ // }
480
+ //}
411
481
return algName == null ? def : algName ;
412
482
}
413
483
@@ -542,6 +612,12 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub
542
612
543
613
final PrivateKey privateKey = ((PKey ) key ).getPrivateKey ();
544
614
try {
615
+ if ( avoidJavaSecurity ) {
616
+ // NOT IMPLEMENTED
617
+ }
618
+ else {
619
+ //crl = generator.generate(((PKey) key).getPrivateKey());
620
+ }
545
621
/*
546
622
AlgorithmIdentifier keyAldID = new AlgorithmIdentifier(new ASN1ObjectIdentifier(keyAlg));
547
623
AlgorithmIdentifier digAldID = new AlgorithmIdentifier(new ASN1ObjectIdentifier(digAlg));
@@ -569,9 +645,9 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub
569
645
debugStackTrace (e ); throw newCRLError (runtime , e .getMessage ());
570
646
}
571
647
572
- final ASN1Primitive crlValue = getCRLValue (runtime );
648
+ final ASN1Primitive crlVal = getCRLValue (runtime );
573
649
574
- ASN1Sequence v1 = (ASN1Sequence ) ( ((ASN1Sequence ) crlValue ).getObjectAt (0 ) );
650
+ ASN1Sequence v1 = (ASN1Sequence ) ( ((ASN1Sequence ) crlVal ).getObjectAt (0 ) );
575
651
final ASN1EncodableVector build1 = new ASN1EncodableVector ();
576
652
int copyIndex = 0 ;
577
653
if ( v1 .getObjectAt (0 ) instanceof ASN1Integer ) copyIndex ++;
@@ -581,8 +657,8 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub
581
657
}
582
658
final ASN1EncodableVector build2 = new ASN1EncodableVector ();
583
659
build2 .add ( new DLSequence (build1 ) );
584
- build2 .add ( ((ASN1Sequence ) crlValue ).getObjectAt (1 ) );
585
- build2 .add ( ((ASN1Sequence ) crlValue ).getObjectAt (2 ) );
660
+ build2 .add ( ((ASN1Sequence ) crlVal ).getObjectAt (1 ) );
661
+ build2 .add ( ((ASN1Sequence ) crlVal ).getObjectAt (2 ) );
586
662
587
663
this .crlValue = new DLSequence (build2 );
588
664
changed = false ;
@@ -595,8 +671,7 @@ private String getSignatureAlgorithm(final Ruby runtime, final PKey key, final D
595
671
final String digAlg = digest .getShortAlgorithm ();
596
672
597
673
if ( "DSA" .equalsIgnoreCase (keyAlg ) ) {
598
- if ( ( "MD5" .equalsIgnoreCase ( digAlg ) ) ||
599
- ( "SHA1" .equals ( digest .name ().toString () ) ) ) {
674
+ if ( ( "MD5" .equalsIgnoreCase ( digAlg ) ) ) {
600
675
throw newCRLError (runtime , "unsupported key / digest algorithm (" + key +" / " + digAlg +")" );
601
676
}
602
677
}
@@ -609,6 +684,10 @@ else if ( "RSA".equalsIgnoreCase(keyAlg) ) {
609
684
return digAlg + "WITH" + keyAlg ;
610
685
}
611
686
687
+ private boolean isDSA (final PKey key ) {
688
+ return "DSA" .equalsIgnoreCase ( key .getAlgorithm () );
689
+ }
690
+
612
691
private ASN1Primitive getCRLValue (final Ruby runtime ) {
613
692
if ( this .crlValue != null ) return this .crlValue ;
614
693
return this .crlValue = readCRL ( runtime );
@@ -627,25 +706,76 @@ public IRubyObject verify(final ThreadContext context, final IRubyObject key) {
627
706
if ( changed ) return context .runtime .getFalse ();
628
707
final PublicKey publicKey = ((PKey ) key ).getPublicKey ();
629
708
try {
630
- boolean valid = SecurityHelper .verify (getCRL (), publicKey , true );
709
+ // NOTE: with BC 1.49 this seems to need BC provider installed ;(
710
+ // java.security.NoSuchProviderException: no such provider: BC
711
+ // at sun.security.jca.GetInstance.getService(GetInstance.java:83)
712
+ // at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
713
+ // at java.security.Signature.getInstance(Signature.java:355)
714
+ // at org.bouncycastle.jcajce.provider.asymmetric.x509.X509CRLObject.verify(Unknown Source)
715
+ // at org.bouncycastle.jcajce.provider.asymmetric.x509.X509CRLObject.verify(Unknown Source)
716
+ // at org.jruby.ext.openssl.SecurityHelper.verify(SecurityHelper.java:564)
717
+ // at org.jruby.ext.openssl.X509CRL.verify(X509CRL.java:717)
718
+ //boolean valid = SecurityHelper.verify(getCRL(), publicKey, true);
719
+
720
+ final DigestAlgorithmIdentifierFinder digestAlgFinder = new DefaultDigestAlgorithmIdentifierFinder ();
721
+ final ContentVerifierProvider verifierProvider ;
722
+ if ( isDSA ( (PKey ) key ) ) {
723
+ BigInteger y = ((DSAPublicKey ) publicKey ).getY ();
724
+ DSAParams params = ((DSAPublicKey ) publicKey ).getParams ();
725
+ DSAParameters parameters = new DSAParameters (params .getP (), params .getQ (), params .getG ());
726
+ AsymmetricKeyParameter dsaKey = new DSAPublicKeyParameters (y , parameters );
727
+ verifierProvider = new BcDSAContentVerifierProviderBuilder (digestAlgFinder ).build (dsaKey );
728
+ }
729
+ else {
730
+ BigInteger mod = ((RSAPublicKey ) publicKey ).getModulus ();
731
+ BigInteger exp = ((RSAPublicKey ) publicKey ).getPublicExponent ();
732
+ AsymmetricKeyParameter rsaKey = new RSAKeyParameters (false , mod , exp );
733
+ verifierProvider = new BcRSAContentVerifierProviderBuilder (digestAlgFinder ).build (rsaKey );
734
+ }
735
+ //final X509CRLHolder crl = getCRLHolder();
736
+ //final AlgorithmIdentifier algId = crl.toASN1Structure().getSignatureAlgorithm();
737
+ boolean valid = getCRLHolder (false ).isSignatureValid ( verifierProvider );
631
738
return context .runtime .newBoolean (valid );
632
739
}
633
- catch (CRLException e ) {
740
+ catch (OperatorException e ) {
634
741
debug ("CRL#verify() failed:" , e );
635
742
return context .runtime .getFalse ();
636
743
}
637
- catch (InvalidKeyException e ) {
744
+ catch (CertException e ) {
638
745
debug ("CRL#verify() failed:" , e );
639
746
return context .runtime .getFalse ();
640
747
}
641
- catch (SignatureException e ) {
642
- debug ("CRL#verify() failed:" , e );
643
- return context .runtime .getFalse ();
748
+ // catch (SignatureException e) {
749
+ // debug("CRL#verify() failed:", e);
750
+ // return context.runtime.getFalse();
751
+ // }
752
+ // catch (NoSuchAlgorithmException e) {
753
+ // return context.runtime.getFalse();
754
+ // }
755
+ }
756
+
757
+ /*
758
+ private static boolean verify(final CertificateList crl, final PublicKey publicKey)
759
+ throws CRLException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
760
+
761
+ final AlgorithmIdentifier tbsSignatureId = crl.getTBSCertList().getSignature();
762
+ if ( ! crl.getSignatureAlgorithm().equals( tbsSignatureId ) ) {
763
+ if ( true ) return false;
764
+ //throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
644
765
}
645
- catch (NoSuchAlgorithmException e ) {
646
- return context .runtime .getFalse ();
766
+
767
+ final String sigAlgName = X509SignatureUtil.getSignatureName(crl.getSignatureAlgorithm());
768
+ final Signature signature = SecurityHelper.getSignature(sigAlgName, securityProvider);
769
+
770
+ signature.initVerify(publicKey);
771
+ signature.update(crl.getTBSCertList());
772
+
773
+ if ( ! signature.verify( crl.getSignature() ) ) {
774
+ if ( true ) return false;
775
+ //throw new SignatureException("CRL does not verify with supplied public key.");
647
776
}
648
- }
777
+ return true;
778
+ } */
649
779
650
780
private static RubyClass _CRLError (final Ruby runtime ) {
651
781
return _X509 (runtime ).getClass ("CRLError" );
0 commit comments