我正在设计一个 API,该 API 要求对加密消息进行签名并通过 TLS 发送。TLS 是 HTTP/SSL。
消息本身是几百 KB(最多)的 JSON 数据,使用非对称密钥加密。PK 基础设施由信任网络解决,并且双方位于同一城市,因此我们可以亲自交换密钥。
我们无法依赖 GnuPG/PGP,因为我们使用的语言中的库支持情况未知,我们更愿意直接使用 OpenSSL。
我们希望以纯文本块的形式发送以下消息及其签名。使用 GnuPG/PGP 可能如下所示:
-----BEGIN PGP SIGNED MESSAGE-----
#
# Encrypted message data here...
#
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (MingW32)
iQIVAwUBPOCy1mM8BmRItYg4AQKWrA//fR5LKFyt+78CMtfpzkHgCVFyEe2ImsBy
FJ2HvzRIP4Bvor1iEOZ9A0fux8gBNXrvEtaDXSiGyXH+Ru4F3g1+K119fgBPRBgo
oOTbSLZSlRYWp8mRALsiWXKEHWgpy4zIHVTY6tPJdxFBZYJXnQj/4S6MRP+eJdam
rU8ufExxaqQPw+KCNEVCSk1yHZ886k6MTSa1oDqUOLiM1cBDCtD8Jv+BE0gLHPb9
1h7lEka8QGNe+P7iiUzvsuD7HCL6dGb6T70/KBBHIP6lDwOgUX3eTd8e+I3jczs9
RyEmd6G4swM3IzCD1km+SN5/k5QsMjd6Lw5fB95Mroi47QNpya8ifYbMgCg0+BVm
c7QOwr79+9cJiKhEICbMf5pKQWzP/AznaYlM0IOGGCvxa5loLl7BbtvktVMocitF
zWM9SB0kmSu3OlMxjXYcBsyHCHN4dTpCD9d1jfbgth9YV06sWpONLohdaWx+n9kO
CxsSDGI+aW8sGKHWonw0Uy4UAvUzY3tiZTzTF+FzoJzhy13KK1j4Y0MMx1jZ68f9
R9wSKVdiyXwuMXkWWK0uxSZuBz4mTofZ7YmFm7UdxOH4bMnO+rWNCSPR7md+X0j1
nQSwtxEnIu7Tucb/ZG3t9kR+KTByPTu7tHINr4HFd8m2Cu7Wi10TP/EBtXbtYA/1
SBaUXcbgCD8=
=Hn6O
-----END PGP SIGNATURE-----
但是我找不到使用 OpenSSL 执行相同操作的合适方法。smime
据我所知,它不合适,因为我们不发送邮件。
有问题的两个部分是:
$ openssl enc -base64 < minimum.json.crypt
sxuEWSFRXxVOylBA/Z38slAQeY9hzypEt9ZmGDSORBID0VMyvbj1bF5jnfA/rpKm
2HsKh8mZq+Gcgf9iPvVoOpzIgAdVqI3CK6CjXCi87y9LL0alYXXFs/ZEQp46URDv
cv8tiOjEnx+u/c1RpyKHHq0QBKStN8jrdQfVCWfoGhw8bS4SfyXxX/OcngKBp094
lcoR9DTz4F/pkvLwrr0wwCMv2/ayqI5Q6ZzB5Y9OUrb4smLNK/Q7PWsO4sYcGoPL
WpmjZ1TNPCvepWQhqEGrVAHxxCdjNczqaRgia5wqZnlLfhCRRHQqADN0nApabAvS
kXerrgcY25Qy4bWjCk/Y0Q==
$ openssl enc -base64 -in minimum.json.sig
onVyfVDakk6DP28T40bt2C2xF+gEZwssemNQIg+pz/PjgTQRTDN42K9p81aSkbkN
Wdlab/TqJCmGNZuIhGKe6f+oDTL6PNofEDr8QGyLzv5Lk+2+/fcD/OV2bI6RV2Ar
95on3jzwKIsaDyMT/9oHtBf7EiZptNLpGp8Fkp+4tfgt7z5w1Di4sA1sCUVdgmsR
KIEa2MaOQxKG9DyrxOz/Fjz7RUPMRjCF+AJ8EFkGfzQmj+PmOxwnRW4nMVVqf858
59NXWy1W5L5Smf1/XC14eLh6LMcmJpHeiEIVu9RMKAVJagMP8eOVxM4QAt1Jvq9W
6Dqmako0I+0Rjt9FxFTRAQ==
该签名是由原始未加密消息体的 SHA1 生成的。
我们的目的是将整个格式化的文档作为 HTTPPOST
主体发送。哪种格式是正确的?有标准吗?
答案1
邮件/多用途邮件是确保MIME 数据,而不是专门用于电子邮件。与 HTTP 一起使用应该不会有任何问题,因为它们都使用相同的 MIME 格式,甚至类似的标头语法。例如:
POST /接收方 HTTP/1.1 主机:example.com 内容类型:multipart/signed;协议=“application/pkcs7-signature”; micalg=sha1;边界=“------------ms040900030501050600040105” 内容长度:476 --------------ms040900030501050600040105 内容类型:text/plain;字符集=UTF-8 内容传输编码:quoted-printable 这是一个示例消息。 --------------ms040900030501050600040105 内容类型:application/pkcs7-signature 内容传输编码:base64 MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIEKDCC BCQwggMMoAMCAQICAiAJMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAYTAkxUMRIwEAYDVQQK (已修剪) --------------ms040900030501050600040105--
(请注意,OpenPGP 也有一个等效项;PGP/MIME,它由大多数支持 PGP 的电子邮件客户端实现,并且可以由 HTTP 使用。)
S/MIME 使用 PKCS#7 作为基础。您也可以使用原始 PKCS#7,无需任何 S/MIME 包装,例如使用openssl smime -pk7out
,它接受 S/MIME 消息并输出 PKCS#7signedData
结构。
openssl smime -sign | openssl smime -pk7out | openssl asn1parse -i
答案2
我最近在 JavaScript 中为 Ajax 帖子做了类似的事情,只需使用 mimi 边界分隔帖子内容,无论使用什么技术,Mime 边界都可以发挥作用。
//简单 JS 示例:
var body = '';
var boundary=Math.random().toString().substr(2);
body += "--"+boundary+"\r\ncontent-disposition: form-data; name=content1;\r\ncontent-type: text/plain\r\n\r\n"+content1+"\r\n";
body += "--"+boundary+"\r\ncontent-disposition: form-data; name=content2;\r\ncontent-type: text/plain\r\n\r\n"+content2+"\r\n";
body += "--"+boundary+"--\r\n";
$.ajax({
url:"/api",
type: 'POST',
context:this,
contentType: 'multipart/form-data; charset=utf-8; boundary=' + boundary,
data: body,
error: error_callback,
success: success_callback
});
服务器应用程序仅使用常规参数接收值
//rack
params[:content1]
params[:content2]
//php
$_POST['content1']
$_POST['content2']