Skip to content

Commit fe3a483

Browse files
committed
Merge pull request #71 from jruby/more-default-locations
More default locations for ca-cert files. matching MRI more closely
2 parents 3dbd038 + 43be800 commit fe3a483

File tree

10 files changed

+237
-20
lines changed

10 files changed

+237
-20
lines changed

src/main/java/org/jruby/ext/openssl/X509StoreContext.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,16 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
114114
if ( args.length > 2) chain = args[2];
115115
}
116116

117-
final X509AuxCertificate _cert = cert.isNil() ? null : ((X509Cert) cert).getAuxCert();
117+
final X509AuxCertificate _cert;
118+
if (cert.isNil()) {
119+
_cert = null;
120+
}
121+
else {
122+
if (! (cert instanceof X509Cert)) {
123+
throw getRuntime().newTypeError(cert, "OpenSSL::X509::Certificate");
124+
}
125+
_cert = ((X509Cert) cert).getAuxCert();
126+
}
118127
final List<X509AuxCertificate> _chain;
119128
if ( ! chain.isNil() ) {
120129
@SuppressWarnings("unchecked")

src/main/java/org/jruby/ext/openssl/x509store/Lookup.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,7 @@ else if ( cert instanceof CRL ) {
352352
}
353353
}
354354

355-
public int loadDefaultJavaCACertsFile() throws IOException, GeneralSecurityException {
356-
final String certsFile = X509Utils.X509_CERT_FILE.replace('/', File.separatorChar);
355+
public int loadDefaultJavaCACertsFile(String certsFile) throws IOException, GeneralSecurityException {
357356
final FileInputStream fin = new FileInputStream(certsFile);
358357
int count = 0;
359358
try {
@@ -517,15 +516,15 @@ public int call(final Lookup ctx, final Integer cmd, final String argp, final Nu
517516
file = ctx.envEntry( getDefaultCertificateFileEnvironment() );
518517
}
519518
catch (RuntimeException e) { }
520-
521-
if (file != null) {
519+
if (file == null) {
520+
file = X509Utils.X509_CERT_FILE.replace('/', File.separatorChar);
521+
}
522+
if (file.matches(".*\\.(crt|cer|pem)$")) {
522523
ok = ctx.loadCertificateOrCRLFile(file, X509_FILETYPE_PEM) != 0 ? 1 : 0;
523524
} else {
524-
ok = (ctx.loadDefaultJavaCACertsFile() != 0) ? 1: 0;
525-
}
526-
if (ok == 0) {
527-
X509Error.addError(X509_R_LOADING_DEFAULTS);
525+
ok = (ctx.loadDefaultJavaCACertsFile(file) != 0) ? 1: 0;
528526
}
527+
// we ignore errors on loading default paths the same way MRI does it
529528
} else {
530529
if (arglInt == X509_FILETYPE_PEM) {
531530
ok = (ctx.loadCertificateOrCRLFile(argp, X509_FILETYPE_PEM) != 0) ? 1 : 0;

src/main/java/org/jruby/ext/openssl/x509store/Store.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static org.jruby.ext.openssl.x509store.X509Utils.X509_R_CERT_ALREADY_IN_HASH_TABLE;
3333

3434
import java.io.FileNotFoundException;
35+
import java.io.IOException;
3536
import java.security.cert.X509Certificate;
3637
import java.util.ArrayList;
3738
import java.util.Arrays;
@@ -372,6 +373,12 @@ public int setDefaultPaths(Ruby runtime) throws Exception {
372373
catch (FileNotFoundException e) {
373374
// set_default_paths ignores FileNotFound
374375
}
376+
catch (IOException e) {
377+
if (!e.getClass().getSimpleName().equals("NotFound")) {
378+
throw e;
379+
}
380+
// set_default_paths ignores FileNotFound
381+
}
375382

376383
lookup = addLookup(runtime, Lookup.hashDirLookup());
377384
//if ( lookup == null ) return 0;
@@ -382,6 +389,12 @@ public int setDefaultPaths(Ruby runtime) throws Exception {
382389
catch (FileNotFoundException e) {
383390
// set_default_paths ignores FileNotFound
384391
}
392+
catch (IOException e) {
393+
if (!e.getClass().getSimpleName().equals("NotFound")) {
394+
throw e;
395+
}
396+
// set_default_paths ignores FileNotFound
397+
}
385398

386399
X509Error.clearErrors();
387400
return 1;

src/main/java/org/jruby/ext/openssl/x509store/X509Utils.java

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
package org.jruby.ext.openssl.x509store;
2929

3030

31+
import java.io.File;
3132
import java.io.IOException;
3233
import java.math.BigInteger;
3334
import java.util.Arrays;
@@ -292,13 +293,62 @@ else if ( keyUsage != null && ! keyUsage[5] ) { // KU_KEY_CERT_SIGN
292293
public static final String X509_PRIVATE_DIR;
293294

294295
static {
295-
OPENSSLDIR = "/usr/local/openssl"; // NOTE: blindly follow?!
296+
// roughly following the ideas from https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
297+
// and falling back to trust store from java to be on the save side
298+
296299
// TODO usability in limited environments should be tested/reviewed
297300
final String JAVA_HOME = SafePropertyAccessor.getProperty("java.home", "");
298-
X509_CERT_AREA = JAVA_HOME + "/lib/security";
299-
X509_CERT_DIR = X509_CERT_AREA;
300-
X509_CERT_FILE = X509_CERT_DIR + "/cacerts";
301-
X509_PRIVATE_DIR = "/usr/lib/ssl/private"; // NOTE: blindly follow?!
301+
302+
// if the default files/dirs exist we use them. with this a switch
303+
// from MRI to JRuby produces the same results. otherwise we use the
304+
// certs from JAVA_HOME.
305+
final String LINUX_CERT_AREA = "/etc/ssl";
306+
final String MACOS_CERT_AREA = "/System/Library/OpenSSL";
307+
308+
String certArea, certDir, privateDir;
309+
String maybeCertFile;
310+
String maybePkiCertFile = "/etc/pki/tls/certs/ca-bundle.crt";
311+
try {
312+
if (new File(LINUX_CERT_AREA).exists()) {
313+
certArea = LINUX_CERT_AREA;
314+
certDir = certArea + "/certs";
315+
privateDir = certArea + "/private";
316+
maybeCertFile = certDir + "/cert.pem";
317+
}
318+
else if (new File(MACOS_CERT_AREA).exists()) {
319+
certArea = MACOS_CERT_AREA;
320+
certDir = certArea + "/certs";
321+
privateDir = certArea + "/private";
322+
maybeCertFile = certArea + "/cert.pem";
323+
}
324+
else {
325+
certArea = JAVA_HOME + "/lib/security";
326+
certDir = certArea;
327+
privateDir = certArea;
328+
maybeCertFile = maybePkiCertFile;
329+
}
330+
}
331+
catch (SecurityException e) {
332+
maybeCertFile = null; maybePkiCertFile = null;
333+
privateDir = certDir = certArea = JAVA_HOME + "/lib/security";
334+
}
335+
336+
X509_CERT_AREA = certArea;
337+
X509_CERT_DIR = certDir;
338+
X509_PRIVATE_DIR = privateDir;
339+
340+
if (maybePkiCertFile != null && new File(maybePkiCertFile).exists()) {
341+
X509_CERT_FILE = maybePkiCertFile;
342+
}
343+
else if (maybeCertFile != null && new File(maybeCertFile).exists()) {
344+
X509_CERT_FILE = maybeCertFile;
345+
}
346+
else {
347+
X509_CERT_FILE = JAVA_HOME + "/lib/security/cacerts";
348+
}
349+
350+
// keep it with some meaninful content as it is a public constant
351+
OPENSSLDIR = X509_CERT_AREA;
302352
}
303353

304354
public static final String X509_CERT_DIR_EVP = "SSL_CERT_DIR";

src/test/ruby/x509/ca.crt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICATCCAWoCCQDbLK0ArLx6CTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
3+
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
4+
cyBQdHkgTHRkMB4XDTE1MDkxMDE2NDIyNVoXDTQzMDEyNjE2NDIyNVowRTELMAkG
5+
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
6+
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy3aH
7+
N4Pci0FwOpfwxFXKQ1IlXWrfUIuWTJs4bPek3kKmM4eJOfMLnQ2f/i0nrI1JHJBn
8+
GTMqiXti8lRhdy0HONVCVolc9rycdIj/batAwbCLxhg5lQMVOWzIJ3fW5R5DEcKK
9+
87PN8Dpf4nn9b2zyszGroExqgVFcp5q2zDzD+b0CAwEAATANBgkqhkiG9w0BAQUF
10+
AAOBgQCu+bibmVksOHFauNRRt2uDLzNw6rDJyL9X3UC6Wu2Dp8Sk44RxR+qNQMS/
11+
CqgW9P7TWZdhsRUcnVRpaRMsr8CpTzHMmKaR0APON7v1C0HaV0CBYfzuNraAnlsq
12+
e7KpTHJ+mwxwVvN1XDv/jGqiw83p1wA4jyxQe59Cmubv+jGksQ==
13+
-----END CERTIFICATE-----

src/test/ruby/x509/digicert.pem

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
3+
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
4+
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
5+
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
6+
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
7+
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
8+
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
9+
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
10+
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
11+
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
12+
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
13+
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
14+
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
15+
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
16+
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
17+
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
18+
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
19+
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
20+
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
21+
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
22+
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
23+
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
24+
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
25+
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
26+
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
27+
8TUoE6smftX3eg==
28+
-----END CERTIFICATE-----

src/test/ruby/x509/gibberish.pem

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
something but not any pem section

src/test/ruby/x509/server.crt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Certificate:
2+
Data:
3+
Version: 1 (0x0)
4+
Serial Number: 1 (0x1)
5+
Signature Algorithm: md5WithRSAEncryption
6+
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
7+
Validity
8+
Not Before: Sep 10 16:45:04 2015 GMT
9+
Not After : Sep 9 16:45:04 2016 GMT
10+
Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=example.com
11+
Subject Public Key Info:
12+
Public Key Algorithm: rsaEncryption
13+
RSA Public Key: (1024 bit)
14+
Modulus (1024 bit):
15+
00:db:95:d0:12:c8:78:dc:eb:c2:6f:4a:ea:d2:cc:
16+
21:e1:e9:16:69:11:84:25:c6:d0:2a:4c:a9:50:7a:
17+
23:cb:89:ba:40:d9:14:7a:72:19:90:44:20:46:b0:
18+
5d:f2:f6:6f:53:e2:f0:6a:e5:1c:0e:08:4e:0e:89:
19+
96:af:97:6b:a3:bd:0a:bb:5f:b8:64:d2:12:ed:66:
20+
2c:64:36:90:41:78:61:95:72:84:da:50:53:f2:a4:
21+
02:a2:b2:9b:ee:7d:d4:bd:26:07:06:28:80:af:16:
22+
e1:3b:73:9a:93:dd:48:98:52:0a:cd:6a:d2:9a:95:
23+
02:a5:2c:b6:b7:eb:1f:de:5d
24+
Exponent: 65537 (0x10001)
25+
Signature Algorithm: md5WithRSAEncryption
26+
76:a4:3c:20:c8:4d:a2:bf:ac:34:93:a4:50:58:68:f1:a6:72:
27+
db:eb:27:00:f4:2d:81:97:cb:b8:7f:a6:21:45:53:dc:2d:a4:
28+
3a:62:66:bb:8b:a3:30:b1:e8:2d:d4:19:0d:36:22:2e:1d:77:
29+
f9:bc:b8:9a:47:7a:60:70:83:04:52:a5:e0:14:c9:2e:18:38:
30+
8a:7b:10:da:0a:48:45:3f:7c:97:65:aa:ea:0c:ae:9f:77:c0:
31+
9f:30:1c:e4:55:29:21:cf:b4:ba:60:72:b2:be:4e:29:9f:d3:
32+
1f:c3:82:ed:04:87:b7:11:3f:4a:71:84:9d:34:b8:bf:4f:41:
33+
f0:ed
34+
-----BEGIN CERTIFICATE-----
35+
MIICDzCCAXgCAQEwDQYJKoZIhvcNAQEEBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
36+
BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
37+
ZDAeFw0xNTA5MTAxNjQ1MDRaFw0xNjA5MDkxNjQ1MDRaMFsxCzAJBgNVBAYTAkFV
38+
MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
39+
IFB0eSBMdGQxFDASBgNVBAMTC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA
40+
A4GNADCBiQKBgQDbldASyHjc68JvSurSzCHh6RZpEYQlxtAqTKlQeiPLibpA2RR6
41+
chmQRCBGsF3y9m9T4vBq5RwOCE4OiZavl2ujvQq7X7hk0hLtZixkNpBBeGGVcoTa
42+
UFPypAKispvufdS9JgcGKICvFuE7c5qT3UiYUgrNatKalQKlLLa36x/eXQIDAQAB
43+
MA0GCSqGSIb3DQEBBAUAA4GBAHakPCDITaK/rDSTpFBYaPGmctvrJwD0LYGXy7h/
44+
piFFU9wtpDpiZruLozCx6C3UGQ02Ii4dd/m8uJpHemBwgwRSpeAUyS4YOIp7ENoK
45+
SEU/fJdlquoMrp93wJ8wHORVKSHPtLpgcrK+Timf0x/Dgu0Eh7cRP0pxhJ00uL9P
46+
QfDt
47+
-----END CERTIFICATE-----

src/test/ruby/x509/store

578 Bytes
Binary file not shown.

src/test/ruby/x509/test_x509store.rb

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,78 @@
33

44
class TestX509Store < TestCase
55

6-
if defined? JRUBY_VERSION
7-
def setup; require 'jopenssl/load' end
8-
else
9-
def setup; require 'openssl' end
6+
def setup
7+
require 'openssl'
8+
@cert = OpenSSL::X509::Certificate.new(File.read(File.expand_path('../server.crt', __FILE__)))
9+
@ca_cert = File.expand_path('../ca.crt', __FILE__)
10+
@javastore = File.expand_path('../store', __FILE__)
11+
@pem = File.expand_path('../EntrustnetSecureServerCertificationAuthority.pem', __FILE__)
12+
end
13+
14+
def test_store_location_with_pem
15+
ENV['SSL_CERT_FILE'] = nil
16+
store = OpenSSL::X509::Store.new
17+
store.set_default_paths
18+
assert !store.verify(@cert)
19+
20+
ENV['SSL_CERT_FILE'] = @ca_cert
21+
store = OpenSSL::X509::Store.new
22+
assert !store.verify(@cert)
23+
store.set_default_paths
24+
assert store.verify(@cert)
25+
end
26+
27+
def test_store_location_with_java_truststore
28+
ENV['SSL_CERT_FILE'] = @javastore
29+
store = OpenSSL::X509::Store.new
30+
assert !store.verify(@cert)
31+
store.set_default_paths
32+
assert store.verify(@cert)
33+
end
34+
35+
def test_use_gibberish_cert_file
36+
ENV['SSL_CERT_FILE'] = File.expand_path('../gibberish.pem', __FILE__)
37+
store = OpenSSL::X509::Store.new
38+
store.set_default_paths
39+
assert !store.verify(@cert)
40+
end
41+
42+
def test_use_default_cert_file_as_custom_file
43+
ENV['SSL_CERT_FILE'] = OpenSSL::X509::DEFAULT_CERT_FILE
44+
store = OpenSSL::X509::Store.new
45+
store.set_default_paths
46+
cert = OpenSSL::X509::Certificate.new(File.read(File.expand_path('../digicert.pem', __FILE__)))
47+
assert store.verify(cert)
48+
end
49+
50+
def test_add_file_to_store_with_custom_cert_file
51+
ENV['SSL_CERT_FILE'] = @ca_cert
52+
store = OpenSSL::X509::Store.new
53+
store.set_default_paths
54+
store.add_file @pem
55+
assert store.verify( OpenSSL::X509::Certificate.new(File.read(@pem)))
56+
end
57+
58+
def test_use_non_existing_cert_file
59+
ENV['SSL_CERT_FILE'] = 'non-existing-file.crt'
60+
store = OpenSSL::X509::Store.new
61+
store.set_default_paths
62+
assert !store.verify(@cert)
63+
end
64+
65+
def test_verfy_with_wrong_argument
66+
store = OpenSSL::X509::Store.new
67+
assert_raise(TypeError) { store.verify( 'not an cert object' ) }
1068
end
1169

1270
def test_add_cert_concurrently
13-
pem = File.expand_path('../EntrustnetSecureServerCertificationAuthority.pem', __FILE__)
1471
store = OpenSSL::X509::Store.new
1572
t = []
1673
(0..25).each do |i|
1774

1875
t << Thread.new do
1976
(0..2).each do
20-
store.add_file pem
77+
store.add_file @pem
2178
end
2279
end
2380
end

0 commit comments

Comments
 (0)