blob: f1b251203a9ba84440c555fecaae169feba176e0 [file] [log] [blame]
Alexander Afanasyev82c359c2017-01-04 14:48:07 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2017 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "ndnsec.hpp"
23#include "util.hpp"
24
25namespace ndn {
26namespace ndnsec {
27
28int
29ndnsec_cert_gen(int argc, char** argv)
30{
31 using boost::tokenizer;
32 using boost::escaped_list_separator;
33
34 namespace po = boost::program_options;
35
36 security::v1::KeyChain keyChain;
37
38 std::string notBeforeStr;
39 std::string notAfterStr;
40 std::string subjectName;
41 std::string requestFile("-");
42 Name signId;
43 std::string subjectInfo;
44 std::vector<std::string> signedInfo;
45 Name certPrefix = security::v1::KeyChain::DEFAULT_PREFIX; // to avoid displaying the default value
46
47 po::options_description description(
48 "General Usage\n"
49 " ndnsec cert-gen [-h] [-S date] [-E date] [-N subject-name] [-I subject-info] "
50 "[-s sign-id] [-p cert-prefix] request\n"
51 "General options");
52
53 description.add_options()
54 ("help,h", "produce help message")
55 ("not-before,S", po::value<std::string>(&notBeforeStr),
56 "certificate starting date, YYYYMMDDhhmmss (default: now)")
57 ("not-after,E", po::value<std::string>(&notAfterStr),
58 "certificate ending date, YYYYMMDDhhmmss (default: now + 365 days)")
59 ("subject-name,N", po::value<std::string>(&subjectName),
60 "subject name")
61 ("subject-info,I", po::value<std::string>(&subjectInfo),
62 "(deprecated, uses 'signed-info') subject info, pairs of OID and string "
63 " description: \"2.5.4.10 'University of California, Los Angeles'\"")
64 ("signed-info", po::value<std::vector<std::string> >(&signedInfo),
65 "a pair of OID and string (must be separated by a single space), e.g., "
66 "\"2.5.4.10 University of California, Los Angeles\". "
67 "May be repeated multiple times")
68 ("sign-id,s", po::value<Name>(&signId)->default_value(keyChain.getDefaultIdentity()),
69 "signing identity")
70 ("cert-prefix,p", po::value<Name>(&certPrefix),
71 "cert prefix, which is the part of certificate name before "
72 "KEY component")
73 ("request,r", po::value<std::string>(&requestFile)->default_value("-"),
74 "request file name, - for stdin")
75 ;
76
77 po::positional_options_description p;
78 p.add("request", 1);
79
80 po::variables_map vm;
81 try {
82 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
83 po::notify(vm);
84 }
85 catch (const std::exception& e) {
86 std::cerr << "ERROR: " << e.what() << std::endl;
87 return 1;
88 }
89
90 if (vm.count("help") != 0) {
91 std::cout << description << std::endl;
92 return 0;
93 }
94
95 if (vm.count("subject-name") == 0) {
96 std::cerr << "ERROR: subject name must be specified" << std::endl
97 << std::endl
98 << description << std::endl;
99 return 1;
100 }
101
102 std::vector<security::v1::CertificateSubjectDescription> subjectDescription;
103 subjectDescription.push_back(security::v1::CertificateSubjectDescription(oid::ATTRIBUTE_NAME, subjectName));
104
105 // 'subjectInfo' is deprecated and the following block will be removed eventually
106 tokenizer<escaped_list_separator<char>> subjectInfoItems(subjectInfo,
107 escaped_list_separator<char>("\\", " \t",
108 "'\""));
109
110 tokenizer<escaped_list_separator<char>>::iterator it = subjectInfoItems.begin();
111
112 while (it != subjectInfoItems.end()) {
113 std::string oid = *it;
114
115 it++;
116 if (it == subjectInfoItems.end()) {
117 std::cerr << "ERROR: unmatched info for oid [" << oid << "]" << std::endl;
118 return 1;
119 }
120
121 std::string value = *it;
122
123 subjectDescription.push_back(security::v1::CertificateSubjectDescription(Oid(oid), value));
124
125 it++;
126 }
127
128 // new 'signedInfo' processing
129 for (std::vector<std::string>::const_iterator info = signedInfo.begin(); info != signedInfo.end();
130 ++info) {
131 size_t pos = info->find(" ");
132 if (pos == std::string::npos) {
133 std::cerr << "ERROR: incorrectly formatted signed info block [" << *info << "]" << std::endl;
134 return 1;
135 }
136 Oid oid(info->substr(0, pos));
137 std::string value = info->substr(pos + 1);
138
139 subjectDescription.push_back(security::v1::CertificateSubjectDescription(oid, value));
140 }
141
142 time::system_clock::TimePoint notBefore;
143 time::system_clock::TimePoint notAfter;
144
145 if (vm.count("not-before") == 0) {
146 notBefore = time::system_clock::now();
147 }
148 else {
149 notBefore = time::fromIsoString(notBeforeStr.substr(0, 8) + "T" + notBeforeStr.substr(8, 6));
150 }
151
152 if (vm.count("not-after") == 0) {
153 notAfter = notBefore + time::days(365);
154 }
155 else {
156 notAfter = time::fromIsoString(notAfterStr.substr(0, 8) + "T" + notAfterStr.substr(8, 6));
157
158 if (notAfter < notBefore) {
159 std::cerr << "ERROR: not-before cannot be later than not-after" << std::endl
160 << std::endl
161 << description << std::endl;
162 return 1;
163 }
164 }
165
166 if (vm.count("request") == 0) {
167 std::cerr << "ERROR: request file must be specified" << std::endl
168 << std::endl
169 << description << std::endl;
170 return 1;
171 }
172
173 shared_ptr<security::v1::IdentityCertificate> selfSignedCertificate = getIdentityCertificate(requestFile);
174
175 if (selfSignedCertificate == nullptr) {
176 std::cerr << "ERROR: input error" << std::endl;
177 return 1;
178 }
179
180 Name keyName = selfSignedCertificate->getPublicKeyName();
181
182 shared_ptr<security::v1::IdentityCertificate> certificate =
183 keyChain.prepareUnsignedIdentityCertificate(keyName, selfSignedCertificate->getPublicKeyInfo(),
184 signId, notBefore, notAfter, subjectDescription,
185 certPrefix);
186
187 if (certificate == nullptr) {
188 std::cerr << "ERROR: key name is not formated correctly or does not match certificate name"
189 << std::endl;
190 return 1;
191 }
192
193 keyChain.createIdentity(signId);
194 Name signingCertificateName = keyChain.getDefaultCertificateNameForIdentity(signId);
195 keyChain.sign(*certificate,
196 security::SigningInfo(security::SigningInfo::SIGNER_TYPE_CERT,
197 signingCertificateName));
198
199 Block wire = certificate->wireEncode();
200
201 namespace t = security::transform;
202 try {
203 t::bufferSource(wire.wire(), wire.size()) >> t::base64Encode(true) >> t::streamSink(std::cout);
204 }
205 catch (const security::transform::Error& e) {
206 std::cerr << "ERROR: " << e.what() << std::endl;
207 return 1;
208 }
209
210 return 0;
211}
212
213} // namespace ndnsec
214} // namespace ndn