Skip to content

Commit 04de740

Browse files
committed
[feat] implement OpenSSL::PKCS7::SignerInfo#signed_time (#269)
1 parent 461ed98 commit 04de740

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929

3030
import java.io.IOException;
3131
import java.io.StringWriter;
32+
import java.text.ParseException;
3233
import java.util.ArrayList;
3334
import java.util.Collection;
35+
import java.util.Date;
3436
import java.util.HashSet;
3537
import java.util.List;
3638

@@ -39,6 +41,7 @@
3941
import java.security.cert.CertificateEncodingException;
4042

4143
import org.bouncycastle.asn1.ASN1Encodable;
44+
import org.bouncycastle.asn1.ASN1UTCTime;
4245

4346
import org.jruby.Ruby;
4447
import org.jruby.RubyArray;
@@ -49,6 +52,7 @@
4952
import org.jruby.RubyNumeric;
5053
import org.jruby.RubyObject;
5154
import org.jruby.RubyString;
55+
import org.jruby.RubyTime;
5256
import org.jruby.anno.JRubyClass;
5357
import org.jruby.anno.JRubyMethod;
5458
import org.jruby.exceptions.RaiseException;
@@ -738,8 +742,20 @@ public IRubyObject serial() {
738742

739743
@JRubyMethod
740744
public IRubyObject signed_time(final ThreadContext context) {
741-
warn(context, "WARNING: unimplemented method called: OpenSSL::PKCS7::SignerInfo#signed_time");
742-
return context.runtime.getNil();
745+
ASN1Encodable asn1obj = info.getSignedAttribute(ASN1Registry.NID_pkcs9_signingTime);
746+
if (asn1obj == null) {
747+
throw newPKCS7Error(context.runtime, "no signing time attribute");
748+
}
749+
if (asn1obj instanceof ASN1UTCTime) {
750+
final Date adjusted;
751+
try {
752+
adjusted = ((ASN1UTCTime) asn1obj).getAdjustedDate();
753+
} catch (ParseException ex) {
754+
throw newPKCS7Error(context.runtime, ex);
755+
}
756+
return RubyTime.newTime(context.runtime, adjusted.getTime());
757+
}
758+
return context.nil;
743759
}
744760
}
745761

src/test/ruby/fixtures/pkey/rsa2048

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
3+
s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
4+
4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
5+
kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
6+
NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
7+
DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
8+
I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
9+
PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
10+
seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
11+
Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
12+
VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
13+
wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
14+
0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
15+
XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
16+
aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
17+
h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
18+
Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
19+
IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
20+
v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
21+
U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
22+
vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
23+
Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
24+
9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
25+
gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
26+
4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
27+
-----END RSA PRIVATE KEY-----

src/test/ruby/pkcs7/test_pkcs7.rb

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# coding: US-ASCII
2+
require File.expand_path('../test_helper', File.dirname(__FILE__))
23

34
module PKCS7Test
45
class TestPKCS7 < TestCase
@@ -889,5 +890,98 @@ def test_enveloped
889890
end
890891

891892
end
893+
894+
# NOTE: based on MRI's test_pkcs7.rb
895+
class TestOpenSSL < TestCase
896+
def setup
897+
@rsa1024 = Fixtures.pkey("rsa1024")
898+
@rsa2048 = Fixtures.pkey("rsa2048")
899+
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
900+
ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
901+
ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
902+
903+
ca_exts = [
904+
["basicConstraints","CA:TRUE",true],
905+
["keyUsage","keyCertSign, cRLSign",true],
906+
["subjectKeyIdentifier","hash",false],
907+
["authorityKeyIdentifier","keyid:always",false],
908+
]
909+
@ca_cert = issue_cert(ca, @rsa2048, 1, ca_exts, nil, nil)
910+
ee_exts = [
911+
["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
912+
["authorityKeyIdentifier","keyid:always",false],
913+
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
914+
]
915+
@ee1_cert = issue_cert(ee1, @rsa1024, 2, ee_exts, @ca_cert, @rsa2048)
916+
@ee2_cert = issue_cert(ee2, @rsa1024, 3, ee_exts, @ca_cert, @rsa2048)
917+
end
918+
919+
def test_signed
920+
store = OpenSSL::X509::Store.new
921+
store.add_cert(@ca_cert)
922+
ca_certs = [@ca_cert]
923+
924+
data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
925+
tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
926+
p7 = OpenSSL::PKCS7.new(tmp.to_der)
927+
certs = p7.certificates
928+
signers = p7.signers
929+
assert(p7.verify([], store))
930+
assert_equal(data, p7.data)
931+
assert_equal(2, certs.size)
932+
assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
933+
assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
934+
assert_equal(1, signers.size)
935+
assert_equal(@ee1_cert.serial, signers[0].serial)
936+
assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
937+
938+
# Normally OpenSSL tries to translate the supplied content into canonical
939+
# MIME format (e.g. a newline character is converted into CR+LF).
940+
# If the content is a binary, PKCS7::BINARY flag should be used.
941+
942+
data = "aaaaa\nbbbbb\nccccc\n"
943+
flag = OpenSSL::PKCS7::BINARY
944+
tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
945+
assert_equal OpenSSL::PKCS7, tmp.class
946+
947+
p7 = OpenSSL::PKCS7.new(tmp.to_der)
948+
certs = p7.certificates
949+
signers = p7.signers
950+
assert(p7.verify([], store))
951+
assert_equal(data, p7.data)
952+
assert_equal(2, certs.size)
953+
assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
954+
assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
955+
assert_equal(1, signers.size)
956+
assert_equal(@ee1_cert.serial, signers[0].serial)
957+
assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
958+
959+
# A signed-data which have multiple signatures can be created
960+
# through the following steps.
961+
# 1. create two signed-data
962+
# 2. copy signerInfo and certificate from one to another
963+
964+
tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag)
965+
tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag)
966+
tmp1.add_signer(tmp2.signers[0])
967+
tmp1.add_certificate(@ee2_cert)
968+
969+
p7 = OpenSSL::PKCS7.new(tmp1.to_der)
970+
certs = p7.certificates
971+
signers = p7.signers
972+
assert(p7.verify([], store))
973+
assert_equal(data, p7.data)
974+
assert_equal(2, certs.size)
975+
assert_equal(2, signers.size)
976+
assert_equal(@ee1_cert.serial, signers[0].serial)
977+
assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
978+
assert_equal(@ee2_cert.serial, signers[1].serial)
979+
assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s)
980+
981+
assert signers[0].signed_time
982+
assert_equal Time, signers[1].signed_time.class
983+
end
984+
985+
end
892986
end
893987

0 commit comments

Comments
 (0)