scclib
Stable Cloud Computing C++ Library
cert.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <crypto/cert.h>
32 #include <gtest/gtest.h>
33 #include <iostream>
34 #include <cstdlib>
35 #include <sstream>
36 #include <vector>
37 
50 using std::cout;
51 using std::endl;
52 using std::string;
53 using std::stringstream;
54 using std::ifstream;
55 using std::vector;
60 using scc::crypto::BasePtr;
61 
62 struct CertTest : public testing::Test
63 {
64  PemDocument doc;
65  PemDocument param;
66  PemDocument priv;
67 
68  string reldir;
69 
70  CertTest()
71  {
72  // ensure the system detects bazel and finds files in the correct location
73  // see https://bazel.build/reference/test-encyclopedia for a description of bazel environment
74  auto sd = getenv("TEST_SRCDIR");
75  if (sd)
76  {
77  cout << "TEST_SRCDIR=" << sd << endl;
78  }
79 
80  stringstream dir;
81 
82  if (sd)
83  {
84  dir << sd << "/com_stablecc_scclib/crypto/unittest/openssl/";
85  }
86  else
87  {
88  dir << "openssl/";
89  }
90 
91  reldir = dir.str();
92  }
93  virtual ~CertTest() {}
94 
95  void load(const std::string& fn, PemDocument& doc)
96  {
97  cout << "loading pem from " << reldir+fn << endl;
98  ifstream f(reldir+fn);
99  ASSERT_NO_THROW(doc.parse(f));
100  }
101  void load(const std::string& fn)
102  {
103  load(fn, doc);
104  }
105  void load_ecc(const std::string& fn) // ecc private key
106  {
107  cout << "loading ecc pem from " << reldir+fn << endl;
108  ifstream f(reldir+fn);
109  // the openssl ecc file is parameters followed by private key
110  ASSERT_NO_THROW(param.parse(f));
111  ASSERT_NO_THROW(priv.parse(f));
112  }
113 
114  void compare(const BasePtr& a, const BasePtr& b)
115  {
116  vector<uint8_t> av, bv;
117  ASSERT_NO_THROW(DerDocument::dump_element(a, av));
118  ASSERT_NO_THROW(DerDocument::dump_element(b, bv));
119  ASSERT_EQ(av, bv);
120  }
121 };
122 
123 TEST_F(CertTest, rsa_certs)
124 {
129 
130  load("osslpub.pem"); // load the openssl public key info file
131  ASSERT_EQ(doc.label(), "PUBLIC KEY");
132 
133  PublicKeyCert cert;
134  cert.parse(doc);
135 
136  cout << cert.str() << endl;
137 
138  ASSERT_EQ(cert.type(), KeyAlgoType::rsa);
139 
140  RsaPublicKey key;
141  cert.get(key); // get the key from the cert
142 
143  cout << key.str() << endl;
144 
145  load("rsapub.pem"); // load the openssl public key
146  ASSERT_EQ(doc.label(), "RSA PUBLIC KEY");
147 
148  RsaPublicKey key2;
149  RsaPublicKeyCert::parse(doc, key2); // get the key from the cert
150 
151  cout << key2.str() << endl;
152 
153  ASSERT_EQ(key, key2); // stored and embedded keys must be the same
154 
155  PublicKeyCert cert2;
156  cert2.set(key);
157 
158  auto d1 = cert.dump();
159  auto d2 = cert2.dump();
160 
161  compare(d1, d2);
162 
163  auto k2 = RsaPublicKeyCert::dump(key);
164 
165  compare(k2, doc.root_ptr());
166 
167  load("rsapriv.pem"); // load the openssl private key file
168  ASSERT_EQ(doc.label(), "RSA PRIVATE KEY");
169 
170  RsaPrivateKey priv;
171  RsaPrivateKeyCert::parse(doc, priv); // pull out the private key
172 
173  cout << priv.str() << endl;
174 
175  ASSERT_EQ(key, priv.pub_key());
176  ASSERT_TRUE(priv.validate(key)); // validate the key pair
177 
178  auto p2 = RsaPrivateKeyCert::dump(priv);
179  compare(p2, doc.root_ptr());
180 }
181 
182 TEST_F(CertTest, ecc_certs)
183 {
188  using scc::crypto::EccGfp;
190  using scc::crypto::Bignum;
191 
192  load("ecpub.pem"); // load the openssl public key info file
193  ASSERT_EQ(doc.label(), "PUBLIC KEY");
194 
195  PublicKeyCert cert;
196  cert.parse(doc);
197 
198  cout << cert.str() << endl;
199 
200  ASSERT_EQ(cert.type(), KeyAlgoType::ec_p521r1);
201 
202  load_ecc("ecpriv.pem");
203  ASSERT_EQ(param.label(), "EC PARAMETERS"); // load the parameters from the first part of openssl private key file
204  ASSERT_EQ(priv.label(), "EC PRIVATE KEY"); // load the private key from second part
205  cout << "** params" << endl;
206  cout << param << endl;
207  cout << "** priv" << endl;
208  cout << priv << endl;
209 
210  KeyAlgoType algo;
211  EcParametersCert::parse(param.root_ptr(), algo); // get the algo from the parameters cert
212  ASSERT_EQ(algo, KeyAlgoType::ec_p521r1);
213 
214  Bignum key;
215  EccGfpPoint pub;
216  EcPrivateKeyCert::parse(priv.root_ptr(), key, algo, pub); // get private, public keys and algo from private cert
217  ASSERT_EQ(algo, KeyAlgoType::ec_p521r1);
218 
219  cout << "*** private cert" << endl;
220  cout << "key: " << key << endl;
221  cout << "algo: " << algo << endl;
222  Bignum x, y;
223  pub.get(x, y);
224  cout << "pub: width=( " << x.width() << " , " << y.width() << " ) val=( " << x << " , " << y << " )" << endl;
225 
226  ASSERT_TRUE(EccGfp::validate_key_pair(key, pub)); // validate the private and public key pair
227 
228  EccGfpPoint pub2;
229  cert.get(pub2); // pull out the public key from the public key certificate
230  ASSERT_EQ(pub, pub2);
231 
232  // dump and compare
233 
234  BasePtr d = EcParametersCert::dump(algo);
235  compare(d, param.root_ptr());
236 
237  BasePtr d2 = EcPrivateKeyCert::dump(key, algo, pub);
238  compare(d2, priv.root_ptr());
239 }
240 
241 struct CaBundleTest : public testing::Test
242 {
243  std::vector<BasePtr> cacerts;
244 
245  CaBundleTest()
246  {
247  ifstream f("/etc/ssl/certs/ca-certificates.crt");
248  while (1)
249  {
250  try
251  {
252  // ca bundle is a bunch of root x.509 certificates added together
253  PemDocument doc;
254  doc.parse(f);
255  cacerts.push_back(doc.root_ptr());
256  }
257  catch (std::exception& ex)
258  {
259  cout << "loaded ca certs bundle, exception: " << ex.what() << endl;
260  break;
261  }
262  }
263  }
264 
265  virtual ~CaBundleTest() {}
266 };
267 
268 TEST_F(CaBundleTest, sanity_test)
269 {
273 
274  cout << "Loaded ca certs, size=" << cacerts.size() << endl;
275 
276  int n = 0;
277  for (auto& c : cacerts)
278  {
279  cout << "*******CA CERT " << ++n << endl;
280 
281  // find the subjectpublickeyinfo element in the x.509 certificate
282  ASSERT_TRUE(c->is_seq());
283  ASSERT_GT(c->contain().size(), 0);
284  ASSERT_TRUE(c->contain()[0]->is_seq());
285  ASSERT_GE(c->contain()[0]->contain().size(), 7);
286 
287  PublicKeyCert cert;
288  cert.parse(c->contain()[0]->contain()[6]);
289  cout << cert.str() << endl;
290 
291  ASSERT_NE(cert.type(), KeyAlgoType::unknown); // we must be able to understand the root keys
292 
293  if (cert.type() == KeyAlgoType::rsa)
294  {
295  RsaPublicKey key;
296  cert.get(key);
297  ASSERT_GT(key.width(), 0);
298  cout << "rsa key width: " << key.width() << endl;
299  }
300  else // this is ecdsa type
301  {
302  EccGfpPoint key;
303  cert.get(key);
304  ASSERT_TRUE(key.valid());
305  cout << "ecdsa point is valid" << endl;
306  }
307  }
308 }
X.509 and RSA certificates.
Big number.
Definition: bignum.h:59
DER document.
Definition: der.h:824
BasePtr root_ptr() const
Return the root pointer.
Definition: der.h:936
Elliptic curve cryptography over Galois prime field GF(p) curve.
Definition: ecc.h:77
PEM formatted DER document.
Definition: der.h:951
virtual void parse(std::istream &)
Parse document from an input stream.
Definition: der.cc:1369
RSA Private Key.
Definition: rsa.h:185
bool validate(const RsaPublicKey &) const
Validate a public key with the private key.
std::string str(unsigned=8) const
Output with formatted values.
RsaPublicKey pub_key() const
Export the public key.
Definition: rsa.h:314
RSA Public Key.
Definition: rsa.h:69
int width() const
Bit width of the key.
std::string str(unsigned=8) const
Output with formatted values.
KeyAlgoType
Key algorithm type.
Definition: cert.h:78
@ rsa
parameter null
Definition: cert.h:80
@ ec_p521r1
parameter {1, 3, 132, 0, 35}
Definition: cert.h:85
From: https://tools.ietf.org/html/rfc3279#section-2.3.5.
Definition: cert.h:210
Private key certificate utility.
Definition: cert.h:298
Public key information certificate.
Definition: cert.h:105
RSA private key certificate.
Definition: cert.h:184
RSA public key certificate.
Definition: cert.h:152