Skip to content

Commit 98fbfc6

Browse files
committed
support PKCS7#decrypt with 1 argument (pkey only - without certificate)
1 parent 3d60286 commit 98fbfc6

4 files changed

Lines changed: 144 additions & 35 deletions

File tree

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

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,15 @@ private static PKCS7 wrap(final Ruby runtime, org.jruby.ext.openssl.impl.PKCS7 p
139139
return wrapped;
140140
}
141141

142-
public static IRubyObject membio2str(Ruby runtime, BIO bio) {
143-
return runtime.newString( new ByteList(((MemBIO) bio).getMemCopy(), false) );
142+
static RubyString membio2str(Ruby runtime, BIO bio, boolean mem) {
143+
final ByteList bytes;
144+
if (mem) {
145+
bytes = new ByteList(((MemBIO) bio).getBuffer(), 0, ((MemBIO) bio).length(), false);
146+
}
147+
else {
148+
bytes = new ByteList(bio.toBytes(), false);
149+
}
150+
return runtime.newString(bytes);
144151
}
145152

146153
private static List<X509AuxCertificate> getAuxCerts(final IRubyObject arg) {
@@ -156,8 +163,8 @@ private static List<X509AuxCertificate> getAuxCerts(final IRubyObject arg) {
156163
public static IRubyObject read_smime(IRubyObject self, IRubyObject arg) {
157164
final Ruby runtime = self.getRuntime();
158165
final BIO in = obj2bio(arg);
159-
final BIO[] out = new BIO[]{ null };
160-
org.jruby.ext.openssl.impl.PKCS7 pkcs7Impl = null;
166+
final BIO[] out = new BIO[] { null };
167+
org.jruby.ext.openssl.impl.PKCS7 pkcs7Impl;
161168
try {
162169
pkcs7Impl = new SMIME(Mime.DEFAULT).readPKCS7(in, out);
163170
}
@@ -170,7 +177,7 @@ public static IRubyObject read_smime(IRubyObject self, IRubyObject arg) {
170177
if ( pkcs7Impl == null ) {
171178
throw newPKCS7Error(runtime, (String) null);
172179
}
173-
IRubyObject data = out[0] != null ? membio2str(runtime, out[0]) : runtime.getNil();
180+
IRubyObject data = out[0] != null ? membio2str(runtime, out[0], false) : runtime.getNil();
174181
final PKCS7 pkcs7 = wrap(runtime, pkcs7Impl);
175182
pkcs7.setData(data);
176183
return pkcs7;
@@ -608,62 +615,59 @@ public IRubyObject verify(IRubyObject[] args) {
608615
catch (NotVerifiedPKCS7Exception e) {
609616
// result = false;
610617
}
611-
catch (PKCS7Exception pkcs7e) {
612-
if ( isDebug(runtime) ) {
613-
// runtime.getOut().println(pkcs7e);
614-
pkcs7e.printStackTrace(runtime.getOut());
615-
}
616-
// result = false;
618+
catch (PKCS7Exception ex) {
619+
OpenSSL.debugStackTrace(ex);
617620
}
618621

619-
IRubyObject data = membio2str(getRuntime(), out);
622+
IRubyObject data = membio2str(runtime, out, true);
620623
setData(data);
621624

622625
return result ? runtime.getTrue() : runtime.getFalse();
623626
}
624627

625628
@JRubyMethod(rest=true)
626-
public IRubyObject decrypt(IRubyObject[] args) {
629+
public IRubyObject decrypt(ThreadContext context, IRubyObject... args) {
627630
IRubyObject dflags;
628-
if ( Arity.checkArgumentCount(getRuntime(), args, 2, 3) == 3 ) {
631+
if ( Arity.checkArgumentCount(context.runtime, args, 1, 3) == 3 ) {
629632
dflags = args[2];
630633
}
631634
else {
632-
dflags = getRuntime().getNil();
635+
dflags = context.nil;
633636
}
634637
PKey pkey = (PKey) args[0];
635-
X509Cert cert = (X509Cert) args[1];
636638

637639
final PrivateKey privKey = pkey.getPrivateKey();
638-
final X509AuxCertificate auxCert = cert.getAuxCert();
639-
final int flg = dflags.isNil() ? 0 : RubyNumeric.fix2int(dflags);
640+
final X509AuxCertificate auxCert = args.length > 1 ? ((X509Cert) args[1]).getAuxCert() : null;
641+
final int flg = dflags == context.nil ? 0 : RubyNumeric.fix2int(dflags);
640642

641643
final BIO out = BIO.mem();
642644
try {
643645
p7.decrypt(privKey, auxCert, out, flg);
644646
}
645-
catch (PKCS7Exception pkcs7e) {
646-
throw newPKCS7Error(getRuntime(), pkcs7e);
647+
catch (PKCS7Exception ex) {
648+
OpenSSL.debugStackTrace(ex);
649+
throw newPKCS7Error(context.runtime, ex);
647650
}
648-
return membio2str(getRuntime(), out);
651+
return membio2str(context.runtime, out, true);
649652
}
650653

651654
@JRubyMethod(name = {"to_pem", "to_s"})
652655
public IRubyObject to_pem() {
653656
StringWriter writer = new StringWriter();
654657
try {
655658
PEMInputOutput.writePKCS7(writer, p7.toASN1());
656-
return getRuntime().newString( writer.toString() );
657659
}
658-
catch (IOException e) {
659-
throw getRuntime().newIOErrorFromException(e);
660+
catch (IOException ex) {
661+
OpenSSL.debugStackTrace(ex);
662+
throw getRuntime().newIOErrorFromException(ex);
660663
}
664+
return StringHelper.newUTF8String(getRuntime(), writer.getBuffer());
661665
}
662666

663667
@JRubyMethod
664668
public IRubyObject to_der() {
665669
try {
666-
return getRuntime().newString(new ByteList(p7.toASN1(), false));
670+
return StringHelper.newString(getRuntime(), p7.toASN1());
667671
}
668672
catch (IOException e) {
669673
throw newPKCS7Error(getRuntime(), e.getMessage());

src/main/java/org/jruby/ext/openssl/impl/EVP.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,14 @@ public static byte[] decrypt(byte[] input, int offset, int len, Key key) throws
108108
NoSuchPaddingException,
109109
IllegalBlockSizeException,
110110
BadPaddingException {
111-
Cipher cipher = SecurityHelper.getCipher(key.getAlgorithm());
111+
String algorithm = key.getAlgorithm();
112+
if ("RSA".equals(algorithm)) {
113+
// with BC we need to get explicit, since:
114+
// RSA is not a regular block cipher, its operation is based on big integer arithmetic.
115+
// As this is the case leading zero bytes will be lost since they are not meaningful
116+
algorithm = "RSA/NONE/PKCS1Padding";
117+
}
118+
Cipher cipher = SecurityHelper.getCipher(algorithm);
112119
cipher.init(Cipher.DECRYPT_MODE, key);
113120
return cipher.doFinal(input, offset, len);
114121
}
@@ -126,10 +133,7 @@ public static byte[] decrypt(byte[] input, Key key) throws InvalidKeyException,
126133

127134
private static String getAlgorithmName(ASN1ObjectIdentifier oid) {
128135
String algorithm = ASN1Registry.o2a(oid);
129-
if (algorithm != null) {
130-
return algorithm.toUpperCase();
131-
} else {
132-
return oid.getId();
133-
}
136+
return (algorithm != null) ? algorithm.toUpperCase() : oid.getId();
134137
}
138+
135139
}// EVP

src/test/ruby/pkcs7/test_pkcs7.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,96 @@ def assert_raise_pkcs7_exception
786786
end
787787
end
788788

789+
public
790+
791+
def test_enveloped
792+
@rsa1024 = OpenSSL::PKey.read <<-_PEM_
793+
-----BEGIN RSA PRIVATE KEY-----
794+
MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
795+
aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
796+
Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
797+
AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
798+
maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
799+
gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
800+
74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
801+
JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
802+
sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
803+
8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
804+
wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
805+
qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
806+
dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
807+
-----END RSA PRIVATE KEY-----
808+
_PEM_
809+
@rsa2048 = OpenSSL::PKey.read <<-_PEM_
810+
-----BEGIN RSA PRIVATE KEY-----
811+
MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
812+
s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
813+
4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
814+
kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
815+
NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
816+
DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
817+
I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
818+
PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
819+
seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
820+
Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
821+
VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
822+
wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
823+
0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
824+
XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
825+
aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
826+
h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
827+
Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
828+
IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
829+
v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
830+
U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
831+
vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
832+
Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
833+
9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
834+
gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
835+
4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
836+
-----END RSA PRIVATE KEY-----
837+
_PEM_
838+
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
839+
ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
840+
ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
841+
842+
ca_exts = [
843+
["basicConstraints","CA:TRUE",true],
844+
["keyUsage","keyCertSign, cRLSign",true],
845+
["subjectKeyIdentifier","hash",false],
846+
["authorityKeyIdentifier","keyid:always",false],
847+
]
848+
@ca_cert = issue_cert(ca, @rsa2048, 1, ca_exts, nil, nil)
849+
ee_exts = [
850+
["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
851+
["authorityKeyIdentifier","keyid:always",false],
852+
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
853+
]
854+
@ee1_cert = issue_cert(ee1, @rsa1024, 2, ee_exts, @ca_cert, @rsa2048)
855+
@ee2_cert = issue_cert(ee2, @rsa1024, 3, ee_exts, @ca_cert, @rsa2048)
856+
857+
#
858+
859+
certs = [@ee1_cert, @ee2_cert]
860+
cipher = OpenSSL::Cipher::AES.new("128-CBC")
861+
data = "aaaaa\nbbbbb\nccccc\n"
862+
863+
tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY)
864+
p7 = OpenSSL::PKCS7.new(tmp.to_der)
865+
recip = p7.recipients
866+
assert_equal(:enveloped, p7.type)
867+
assert_equal(2, recip.size)
868+
869+
assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s)
870+
assert_equal(2, recip[0].serial)
871+
assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert))
872+
873+
assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
874+
assert_equal(3, recip[1].serial)
875+
assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
876+
assert_equal(data, p7.decrypt(@rsa1024))
877+
end
878+
789879
end
790880
end
791881

src/test/ruby/test_helper.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,28 @@ def jruby?; self.class.jruby? end
139139

140140
def debug(msg); puts msg if $VERBOSE end
141141

142-
def issue_cert(dn, key, serial, not_before, not_after, extensions, issuer, issuer_key, digest)
142+
def issue_cert(*args)
143+
# def issue_cert(dn, key, serial, not_before, not_after, extensions, issuer, issuer_key, digest)
144+
# def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
145+
# not_before: nil, not_after: nil, digest: "sha256")
146+
if args.length == 9
147+
dn, key, serial, not_before, not_after, extensions, issuer, issuer_key, digest = *args
148+
else
149+
dn, key, serial, extensions, issuer, issuer_key, opts = *args
150+
opts ||= {}
151+
not_before, not_after, digest = opts[:not_before], opts[:not_after], opts[:digest] || "sha256"
152+
end
143153
cert = OpenSSL::X509::Certificate.new
144154
issuer = cert unless issuer
145155
issuer_key = key unless issuer_key
146156
cert.version = 2
147157
cert.serial = serial
148158
cert.subject = dn
149159
cert.issuer = issuer.subject
150-
cert.public_key = key.public_key
151-
cert.not_before = not_before
152-
cert.not_after = not_after
160+
cert.public_key = key
161+
now = Time.now
162+
cert.not_before = not_before || now - 3600
163+
cert.not_after = not_after || now + 3600
153164
ef = OpenSSL::X509::ExtensionFactory.new
154165
ef.subject_certificate = cert
155166
ef.issuer_certificate = issuer

0 commit comments

Comments
 (0)