Skip to content

Commit 7e53018

Browse files
committed
handle OpenSSL::Cipher.new('aes-128-gcm') with IV under JRuby
... so that it works ~ more compatibly with C OpenSSL (iv_length == 12)
1 parent 4d5bead commit 7e53018

File tree

2 files changed

+57
-8
lines changed

2 files changed

+57
-8
lines changed

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,22 @@
3838
import java.security.MessageDigest;
3939
import java.security.NoSuchAlgorithmException;
4040
import java.security.Provider;
41+
import java.security.spec.AlgorithmParameterSpec;
4142
import java.util.HashMap;
4243
import java.util.LinkedHashMap;
4344
import java.util.List;
4445
import java.util.Map;
4546
import static javax.crypto.Cipher.DECRYPT_MODE;
4647
import static javax.crypto.Cipher.ENCRYPT_MODE;
4748
import javax.crypto.NoSuchPaddingException;
49+
import javax.crypto.spec.GCMParameterSpec;
4850
import javax.crypto.spec.IvParameterSpec;
4951
import javax.crypto.spec.RC2ParameterSpec;
5052

5153
import org.jruby.Ruby;
5254
import org.jruby.RubyArray;
5355
import org.jruby.RubyClass;
56+
import org.jruby.RubyInteger;
5457
import org.jruby.RubyModule;
5558
import org.jruby.RubyNumeric;
5659
import org.jruby.RubyObject;
@@ -611,7 +614,9 @@ public int getIvLength() {
611614

612615
if ( ivLength == -1 ) {
613616
if ( "AES".equals(base) ) {
614-
ivLength = 16;
617+
ivLength = 16; // OpenSSL defaults to 12
618+
// NOTE: we can NOT handle 12 for non GCM mode
619+
if ( "GCM".equals(mode) ) ivLength = 12;
615620
}
616621
//else if ( "DES".equals(base) ) {
617622
// ivLength = 8;
@@ -790,22 +795,22 @@ public IRubyObject initialize_copy(final IRubyObject obj) {
790795
}
791796

792797
@JRubyMethod
793-
public IRubyObject name() {
798+
public final RubyString name() {
794799
return getRuntime().newString(name);
795800
}
796801

797802
@JRubyMethod
798-
public IRubyObject key_len() {
803+
public final RubyInteger key_len() {
799804
return getRuntime().newFixnum(keyLength);
800805
}
801806

802807
@JRubyMethod
803-
public IRubyObject iv_len() {
808+
public final RubyInteger iv_len() {
804809
return getRuntime().newFixnum(ivLength);
805810
}
806811

807812
@JRubyMethod(name = "key_len=", required = 1)
808-
public IRubyObject set_key_len(IRubyObject len) {
813+
public final IRubyObject set_key_len(IRubyObject len) {
809814
this.keyLength = RubyNumeric.fix2int(len);
810815
return len;
811816
}
@@ -959,7 +964,7 @@ private void updateCipher(final String name, final String padding) {
959964
cipher = getCipherInstance();
960965
}
961966

962-
javax.crypto.Cipher getCipherInstance() {
967+
final javax.crypto.Cipher getCipherInstance() {
963968
try {
964969
return getCipherInstance(realName, false);
965970
}
@@ -1040,15 +1045,22 @@ else if ( "RC4".equalsIgnoreCase(cryptoBase) ) {
10401045
);
10411046
}
10421047
else {
1048+
final AlgorithmParameterSpec ivSpec;
1049+
if ( "GCM".equalsIgnoreCase(cryptoMode) ) { // e.g. 'aes-128-gcm'
1050+
ivSpec = new GCMParameterSpec(this.ivLength * 8, this.realIV);
1051+
}
1052+
else {
1053+
ivSpec = new IvParameterSpec(this.realIV);
1054+
}
10431055
cipher.init(encryptMode ? ENCRYPT_MODE : DECRYPT_MODE,
10441056
new SimpleSecretKey(getCipherAlgorithm(), this.key),
1045-
new IvParameterSpec(this.realIV)
1057+
ivSpec
10461058
);
10471059
}
10481060
}
10491061
}
10501062
catch (InvalidKeyException e) {
1051-
throw newCipherError(runtime, e + ": possibly you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your JRE");
1063+
throw newCipherError(runtime, e + "\n possibly you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your JRE");
10521064
}
10531065
catch (Exception e) {
10541066
debugStackTrace(runtime, e);

src/test/ruby/test_cipher.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,43 @@ def test_encrypt_aes_cfb_4_incompatibility
312312
end
313313
end
314314

315+
def test_aes_128_gcm
316+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
317+
assert_equal cipher, cipher.encrypt
318+
cipher.key = '01' * 8
319+
cipher.iv = '0' * 16
320+
321+
bytes = '0000' * 4
322+
expected = "\xAC\xC8\x0E\xEDbX,\xB4\xCD\x02\x06O(p\xF8u" # from MRI
323+
actual = cipher.update(bytes)
324+
assert_equal expected, actual
325+
assert_equal "", cipher.final unless defined? JRUBY_VERSION
326+
327+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
328+
assert_equal cipher, cipher.encrypt
329+
cipher.key = '01' * 8
330+
cipher.iv = '012345678' * 2
331+
332+
bytes = '0000' * 4
333+
expected = "\xF3\xEF\xE6K\xBAJ\xAB=7m'\b\xE0\x06U\x9B" # from MRI
334+
actual = cipher.update(bytes)
335+
assert_equal expected, actual
336+
#assert_equal "", cipher.final unless defined? JRUBY_VERSION
337+
338+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
339+
assert_equal cipher, cipher.encrypt
340+
assert_equal 16, cipher.key_len
341+
assert_equal 12, cipher.iv_len
342+
cipher.key = '01' * 8
343+
cipher.iv = '0' * 12
344+
345+
bytes = '0000' * 4
346+
expected = "\xAC\xC8\x0E\xEDbX,\xB4\xCD\x02\x06O(p\xF8u" # from MRI
347+
actual = cipher.update(bytes)
348+
assert_equal expected, actual
349+
#assert_equal "", cipher.final
350+
end
351+
315352
def test_encrypt_aes_cfb_16_incompatibility
316353
cipher = OpenSSL::Cipher.new 'AES-128-CFB'
317354
assert_equal cipher, cipher.encrypt

0 commit comments

Comments
 (0)