blob: 1b81fc5c28d188767fd765a325d0c1ae0fd08b7b [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yue6bfab22014-02-06 16:01:19 -080022 */
23
Yingdi Yu8d7468f2014-02-21 14:49:45 -080024#ifndef NDNSEC_CERT_GEN_HPP
25#define NDNSEC_CERT_GEN_HPP
Yingdi Yue6bfab22014-02-06 16:01:19 -080026
Yingdi Yu8d7468f2014-02-21 14:49:45 -080027#include "ndnsec-util.hpp"
Yingdi Yue6bfab22014-02-06 16:01:19 -080028
Yingdi Yub61f5402014-02-26 17:46:11 -080029int
Yingdi Yu8d7468f2014-02-21 14:49:45 -080030ndnsec_cert_gen(int argc, char** argv)
Yingdi Yue6bfab22014-02-06 16:01:19 -080031{
Yingdi Yu8d7468f2014-02-21 14:49:45 -080032 using boost::tokenizer;
33 using boost::escaped_list_separator;
Yingdi Yue6bfab22014-02-06 16:01:19 -080034
Yingdi Yu8d7468f2014-02-21 14:49:45 -080035 using namespace ndn;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -070036 using namespace ndn::time;
Yingdi Yu8d7468f2014-02-21 14:49:45 -080037 namespace po = boost::program_options;
Yingdi Yue6bfab22014-02-06 16:01:19 -080038
Yingdi Yue6bfab22014-02-06 16:01:19 -080039 std::string notBeforeStr;
40 std::string notAfterStr;
Yingdi Yub61f5402014-02-26 17:46:11 -080041 std::string subjectName;
42 std::string requestFile("-");
Yingdi Yue6bfab22014-02-06 16:01:19 -080043 std::string signId;
Yingdi Yub61f5402014-02-26 17:46:11 -080044 std::string subjectInfo;
Yingdi Yu05842f22014-04-15 19:21:56 -070045 bool hasSignId = false;
Yingdi Yub61f5402014-02-26 17:46:11 -080046 bool isNack = false;
Yingdi Yue6bfab22014-02-06 16:01:19 -080047
Yingdi Yub61f5402014-02-26 17:46:11 -080048 po::options_description description("General Usage\n ndnsec cert-gen [-h] [-S date] [-E date] [-N subject-name] [-I subject-info] [-s sign-id] request\nGeneral options");
49 description.add_options()
Yingdi Yue6bfab22014-02-06 16:01:19 -080050 ("help,h", "produce help message")
51 ("not-before,S", po::value<std::string>(&notBeforeStr), "certificate starting date, YYYYMMDDhhmmss")
52 ("not-after,E", po::value<std::string>(&notAfterStr), "certificate ending date, YYYYMMDDhhmmss")
Yingdi Yub61f5402014-02-26 17:46:11 -080053 ("subject-name,N", po::value<std::string>(&subjectName), "subject name")
54 ("subject-info,I", po::value<std::string>(&subjectInfo), "subject info, pairs of OID and string description: \"2.5.4.10 'University of California, Los Angeles'\"")
Yingdi Yue6bfab22014-02-06 16:01:19 -080055 ("nack", "Generate revocation certificate (NACK)")
Yingdi Yu05842f22014-04-15 19:21:56 -070056 ("sign-id,s", po::value<std::string>(&signId), "signing Identity, system default identity if not specified")
Yingdi Yub61f5402014-02-26 17:46:11 -080057 ("request,r", po::value<std::string>(&requestFile), "request file name, - for stdin")
Yingdi Yue6bfab22014-02-06 16:01:19 -080058 ;
59
60 po::positional_options_description p;
61 p.add("request", 1);
62
63 po::variables_map vm;
64 try
65 {
Yingdi Yub61f5402014-02-26 17:46:11 -080066 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(),
67 vm);
Yingdi Yue6bfab22014-02-06 16:01:19 -080068 po::notify(vm);
69 }
Yingdi Yub61f5402014-02-26 17:46:11 -080070 catch (const std::exception& e)
Yingdi Yue6bfab22014-02-06 16:01:19 -080071 {
72 std::cerr << "ERROR: " << e.what() << std::endl;
73 return 1;
74 }
75
Yingdi Yub61f5402014-02-26 17:46:11 -080076 if (vm.count("help") != 0)
Yingdi Yue6bfab22014-02-06 16:01:19 -080077 {
Yingdi Yub61f5402014-02-26 17:46:11 -080078 std::cerr << description << std::endl;
Yingdi Yu8d7468f2014-02-21 14:49:45 -080079 return 0;
Yingdi Yue6bfab22014-02-06 16:01:19 -080080 }
81
Yingdi Yu05842f22014-04-15 19:21:56 -070082 if (vm.count("sign-id") != 0)
Yingdi Yub61f5402014-02-26 17:46:11 -080083 {
Yingdi Yu05842f22014-04-15 19:21:56 -070084 hasSignId = true;
Yingdi Yue6bfab22014-02-06 16:01:19 -080085 }
86
Yingdi Yub61f5402014-02-26 17:46:11 -080087 if (vm.count("nack") != 0)
Yingdi Yue6bfab22014-02-06 16:01:19 -080088 {
Yingdi Yub61f5402014-02-26 17:46:11 -080089 isNack = true;
Yingdi Yue6bfab22014-02-06 16:01:19 -080090 }
91
92 std::vector<CertificateSubjectDescription> otherSubDescrypt;
Yingdi Yub61f5402014-02-26 17:46:11 -080093 tokenizer<escaped_list_separator<char> > subjectInfoItems
94 (subjectInfo, escaped_list_separator<char> ("\\", " \t", "'\""));
Yingdi Yue6bfab22014-02-06 16:01:19 -080095
Yingdi Yub61f5402014-02-26 17:46:11 -080096 tokenizer<escaped_list_separator<char> >::iterator it =
97 subjectInfoItems.begin();
98
99 while (it != subjectInfoItems.end())
Yingdi Yue6bfab22014-02-06 16:01:19 -0800100 {
Yingdi Yub61f5402014-02-26 17:46:11 -0800101 std::string oid = *it;
102
103 it++;
104 if (it == subjectInfoItems.end())
Yingdi Yue6bfab22014-02-06 16:01:19 -0800105 {
Yingdi Yub61f5402014-02-26 17:46:11 -0800106 std::cerr << "ERROR: unmatched info for oid [" << oid << "]" << std::endl;
107 return 1;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800108 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800109
110 std::string value = *it;
111
112 otherSubDescrypt.push_back(CertificateSubjectDescription(oid, value));
113
114 it++;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800115 }
116
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700117 system_clock::TimePoint notBefore;
118 system_clock::TimePoint notAfter;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800119
Yingdi Yub61f5402014-02-26 17:46:11 -0800120 if (vm.count("not-before") == 0)
121 {
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700122 notBefore = system_clock::now();
Yingdi Yub61f5402014-02-26 17:46:11 -0800123 }
124 else
125 {
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700126 notBefore = fromIsoString(notBeforeStr.substr(0, 8) + "T" +
127 notBeforeStr.substr(8, 6));
Yingdi Yub61f5402014-02-26 17:46:11 -0800128 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800129
Yingdi Yub61f5402014-02-26 17:46:11 -0800130 if (vm.count("not-after") == 0)
131 {
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700132 notAfter = notBefore + days(365);
Yingdi Yub61f5402014-02-26 17:46:11 -0800133 }
134 else
135 {
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700136 notAfter = fromIsoString(notAfterStr.substr(0, 8) + "T" +
137 notAfterStr.substr(8, 6));
Yingdi Yue6bfab22014-02-06 16:01:19 -0800138
Yingdi Yub61f5402014-02-26 17:46:11 -0800139 if (notAfter < notBefore)
140 {
141 std::cerr << "not-before is later than not-after" << std::endl;
142 return 1;
143 }
144 }
145
146 if (vm.count("request") == 0)
Yingdi Yue6bfab22014-02-06 16:01:19 -0800147 {
148 std::cerr << "request file must be specified" << std::endl;
149 return 1;
150 }
151
Yingdi Yub61f5402014-02-26 17:46:11 -0800152 shared_ptr<IdentityCertificate> selfSignedCertificate
153 = getIdentityCertificate(requestFile);
154
155 if (!static_cast<bool>(selfSignedCertificate))
Yingdi Yue6bfab22014-02-06 16:01:19 -0800156 {
157 std::cerr << "ERROR: input error" << std::endl;
158 return 1;
159 }
160
Yingdi Yu05842f22014-04-15 19:21:56 -0700161 KeyChain keyChain;
162
Yingdi Yue6bfab22014-02-06 16:01:19 -0800163 Name keyName = selfSignedCertificate->getPublicKeyName();
164 Name signIdName;
165 Name certName;
166
Yingdi Yu05842f22014-04-15 19:21:56 -0700167 if (!hasSignId)
168 signIdName = keyChain.getDefaultIdentity();
169 else
170 signIdName = Name(signId);
171
172
173 if (signIdName.isPrefixOf(keyName))
Yingdi Yue6bfab22014-02-06 16:01:19 -0800174 {
Yingdi Yu05842f22014-04-15 19:21:56 -0700175 // if signee's namespace is a sub-namespace of signer, for example, signer's namespace is
176 // /ndn/test, signee's namespace is /ndn/test/alice, the generated certificate name is
177 // /ndn/test/KEY/alice/ksk-1234/ID-CERT/%01%02
178 certName.append(signIdName)
179 .append("KEY")
180 .append(keyName.getSubName(signIdName.size()))
181 .append("ID-CERT")
182 .appendVersion();
Yingdi Yue6bfab22014-02-06 16:01:19 -0800183 }
184 else
185 {
Yingdi Yu05842f22014-04-15 19:21:56 -0700186 // if signee's namespace is not a sub-namespace of signer, for example, signer's namespace is
187 // /ndn/test, signee's namespace is /ndn/ucla/bob, the generated certificate name is
188 // /ndn/ucla/bob/KEY/ksk-1234/ID-CERT/%01%02
189 certName.append(keyName.getPrefix(-1))
190 .append("KEY")
191 .append(keyName.get(-1))
Yingdi Yub61f5402014-02-26 17:46:11 -0800192 .append("ID-CERT")
Yingdi Yu05842f22014-04-15 19:21:56 -0700193 .appendVersion();
Yingdi Yue6bfab22014-02-06 16:01:19 -0800194 }
195
196 Block wire;
197
Yingdi Yub61f5402014-02-26 17:46:11 -0800198 if (!isNack)
Yingdi Yue6bfab22014-02-06 16:01:19 -0800199 {
Yingdi Yub61f5402014-02-26 17:46:11 -0800200 if (vm.count("subject-name") == 0)
Yingdi Yue6bfab22014-02-06 16:01:19 -0800201 {
202 std::cerr << "subject_name must be specified" << std::endl;
203 return 1;
204 }
205
Yingdi Yub61f5402014-02-26 17:46:11 -0800206 CertificateSubjectDescription subDescryptName("2.5.4.41", subjectName);
207 IdentityCertificate certificate;
208 certificate.setName(certName);
209 certificate.setNotBefore(notBefore);
210 certificate.setNotAfter(notAfter);
211 certificate.setPublicKeyInfo(selfSignedCertificate->getPublicKeyInfo());
212 certificate.addSubjectDescription(subDescryptName);
213 for (size_t i = 0; i < otherSubDescrypt.size(); i++)
214 certificate.addSubjectDescription(otherSubDescrypt[i]);
215 certificate.encode();
Yingdi Yue6bfab22014-02-06 16:01:19 -0800216
Yingdi Yu05842f22014-04-15 19:21:56 -0700217 keyChain.createIdentity(signIdName);
218 Name signingCertificateName = keyChain.getDefaultCertificateNameForIdentity(signIdName);
219 keyChain.sign(certificate, signingCertificateName);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800220
Yingdi Yub61f5402014-02-26 17:46:11 -0800221 wire = certificate.wireEncode();
Yingdi Yue6bfab22014-02-06 16:01:19 -0800222 }
223 else
224 {
225 Data revocationCert;
226 // revocationCert.setContent(void*, 0); // empty content
227 revocationCert.setName(certName);
228
Yingdi Yu05842f22014-04-15 19:21:56 -0700229 keyChain.createIdentity(signIdName);
230 Name signingCertificateName = keyChain.getDefaultCertificateNameForIdentity(signIdName);
Yingdi Yub61f5402014-02-26 17:46:11 -0800231 keyChain.sign(revocationCert, signingCertificateName);
Yingdi Yu05842f22014-04-15 19:21:56 -0700232
Yingdi Yub61f5402014-02-26 17:46:11 -0800233 wire = revocationCert.wireEncode();
Yingdi Yue6bfab22014-02-06 16:01:19 -0800234 }
235
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800236 try
237 {
238 using namespace CryptoPP;
239 StringSource ss(wire.wire(), wire.size(), true,
240 new Base64Encoder(new FileSink(std::cout), true, 64));
241 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800242 catch (const CryptoPP::Exception& e)
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800243 {
244 std::cerr << "ERROR: " << e.what() << std::endl;
245 return 1;
246 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800247
248 return 0;
249}
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800250
251#endif //NDNSEC_CERT_GEN_HPP