1 //! This module describes a public key that is restricted to one of the supported algorithms.
2
3 use anyhow::{ensure, Context, Result};
4 use openssl::hash::MessageDigest;
5 use openssl::nid::Nid;
6 use openssl::pkey::{HasParams, Id, PKey, PKeyRef, Public};
7 use openssl::sign::Verifier;
8 use std::error::Error;
9 use std::fmt;
10
11 /// The kinds of digital signature keys that are supported.
12 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
13 pub enum SignatureKind {
14 /// Edwards-curve Digital Signature Algorithm Ed25519.
15 Ed25519,
16 /// Elliptic Curve Digital Signature Algorithm (ECDSA).
17 Ec(EcKind),
18 }
19
20 /// The kinds of key agreement keys that are supported.
21 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
22 pub(crate) enum KeyAgreementKind {
23 X25519,
24 Ec(EcKind),
25 }
26
27 /// Enumeration of the kinds of elliptic curve keys that are supported.
28 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29 pub enum EcKind {
30 /// P-256 elliptic curve.
31 P256,
32 /// P-384 elliptic curve.
33 P384,
34 }
35
36 // Wraps PKey<Public> so we can implement some traits around it, allowing for derived traits on
37 // types that include a PKey<Public>.
38 #[derive(Clone)]
39 struct PKeyPublicWrapper(PKey<Public>);
40
41 /// Public key used for signature validation.
42 #[derive(Clone, Debug, Eq, PartialEq)]
43 pub struct PublicKey {
44 kind: SignatureKind,
45 pkey: PKeyPublicWrapper,
46 }
47
48 /// Public key used for key agreement.
49 #[derive(Clone, Debug, Eq, PartialEq)]
50 pub struct KeyAgreementPublicKey {
51 kind: KeyAgreementKind,
52 pkey: PKeyPublicWrapper,
53 }
54
55 impl PublicKey {
56 /// The signature kind of this key.
kind(&self) -> SignatureKind57 pub fn kind(&self) -> SignatureKind {
58 self.kind
59 }
60
61 /// Reference to the underlying public key.
pkey(&self) -> &PKeyRef<Public>62 pub fn pkey(&self) -> &PKeyRef<Public> {
63 &self.pkey.0
64 }
65
66 /// Verify that the signature obtained from signing the given message
67 /// with the PublicKey matches the signature provided.
verify(&self, signature: &[u8], message: &[u8]) -> Result<()>68 pub fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> {
69 let mut verifier = match self.kind {
70 SignatureKind::Ed25519 => Verifier::new_without_digest(&self.pkey.0),
71 SignatureKind::Ec(ec) => Verifier::new(digest_for_ec(ec), &self.pkey.0),
72 }
73 .with_context(|| format!("Failed to create verifier {:?}", self.kind))?;
74 let verified =
75 verifier.verify_oneshot(signature, message).context("Failed to verify signature")?;
76 ensure!(verified, "Signature verification failed.");
77 Ok(())
78 }
79
80 /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
to_pem(&self) -> String81 pub fn to_pem(&self) -> String {
82 self.pkey.to_pem()
83 }
84
pkey_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<SignatureKind>85 fn pkey_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<SignatureKind> {
86 match pkey.id() {
87 Id::ED25519 => Some(SignatureKind::Ed25519),
88 Id::EC => pkey_ec_kind(pkey).map(SignatureKind::Ec),
89 _ => None,
90 }
91 }
92 }
93
94 impl KeyAgreementPublicKey {
pkey(&self) -> &PKeyRef<Public>95 pub(crate) fn pkey(&self) -> &PKeyRef<Public> {
96 &self.pkey.0
97 }
98
99 /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
to_pem(&self) -> String100 pub fn to_pem(&self) -> String {
101 self.pkey.to_pem()
102 }
103
pkey_kind(pkey: &PKeyRef<Public>) -> Option<KeyAgreementKind>104 fn pkey_kind(pkey: &PKeyRef<Public>) -> Option<KeyAgreementKind> {
105 match pkey.id() {
106 Id::X25519 => Some(KeyAgreementKind::X25519),
107 Id::EC => pkey_ec_kind(pkey).map(KeyAgreementKind::Ec),
108 _ => None,
109 }
110 }
111 }
112
113 impl Eq for PKeyPublicWrapper {}
114
115 impl PartialEq for PKeyPublicWrapper {
eq(&self, rhs: &PKeyPublicWrapper) -> bool116 fn eq(&self, rhs: &PKeyPublicWrapper) -> bool {
117 self.0.public_eq(&rhs.0)
118 }
119 }
120
121 impl PKeyPublicWrapper {
to_pem(&self) -> String122 fn to_pem(&self) -> String {
123 String::from_utf8(self.0.public_key_to_pem().unwrap()).unwrap()
124 }
125 }
126
127 impl fmt::Debug for PKeyPublicWrapper {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result128 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
129 fmt.write_str(&self.to_pem())
130 }
131 }
132
133 /// The error type returned when converting from [`PKey'] to [`PublicKey`] fails.
134 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
135 pub struct TryFromPKeyError(());
136
137 impl fmt::Display for TryFromPKeyError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result138 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(fmt, "unsupported public key conversion attempted")
140 }
141 }
142
143 impl Error for TryFromPKeyError {}
144
145 impl TryFrom<PKey<Public>> for PublicKey {
146 type Error = TryFromPKeyError;
147
try_from(pkey: PKey<Public>) -> Result<Self, Self::Error>148 fn try_from(pkey: PKey<Public>) -> Result<Self, Self::Error> {
149 let kind = PublicKey::pkey_kind(&pkey).ok_or(TryFromPKeyError(()))?;
150 Ok(Self { kind, pkey: PKeyPublicWrapper(pkey) })
151 }
152 }
153
154 impl TryFrom<PKey<Public>> for KeyAgreementPublicKey {
155 type Error = TryFromPKeyError;
156
try_from(pkey: PKey<Public>) -> Result<Self, Self::Error>157 fn try_from(pkey: PKey<Public>) -> Result<Self, Self::Error> {
158 let kind = KeyAgreementPublicKey::pkey_kind(&pkey).ok_or(TryFromPKeyError(()))?;
159 Ok(Self { kind, pkey: PKeyPublicWrapper(pkey) })
160 }
161 }
162
pkey_ec_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<EcKind>163 fn pkey_ec_kind<T: HasParams>(pkey: &PKeyRef<T>) -> Option<EcKind> {
164 match pkey.id() {
165 Id::EC => match pkey.ec_key().unwrap().group().curve_name() {
166 Some(Nid::X9_62_PRIME256V1) => Some(EcKind::P256),
167 Some(Nid::SECP384R1) => Some(EcKind::P384),
168 _ => None,
169 },
170 _ => None,
171 }
172 }
173
digest_for_ec(ec: EcKind) -> MessageDigest174 fn digest_for_ec(ec: EcKind) -> MessageDigest {
175 match ec {
176 EcKind::P256 => MessageDigest::sha256(),
177 EcKind::P384 => MessageDigest::sha384(),
178 }
179 }
180
181 #[cfg(test)]
182 mod tests {
183 use super::*;
184
185 #[test]
from_ed25519_pkey()186 fn from_ed25519_pkey() {
187 let pkey = load_public_pkey(testkeys::ED25519_KEY_PEM[0]);
188 let key: PublicKey = pkey.clone().try_into().unwrap();
189 assert_eq!(key.kind, SignatureKind::Ed25519);
190 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
191 }
192
193 #[test]
from_p256_pkey()194 fn from_p256_pkey() {
195 let pkey = load_public_pkey(testkeys::P256_KEY_PEM[0]);
196 let key: PublicKey = pkey.clone().try_into().unwrap();
197 assert_eq!(key.kind, SignatureKind::Ec(EcKind::P256));
198 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
199 }
200
201 #[test]
from_p384_pkey()202 fn from_p384_pkey() {
203 let pkey = load_public_pkey(testkeys::P384_KEY_PEM[0]);
204 let key: PublicKey = pkey.clone().try_into().unwrap();
205 assert_eq!(key.kind, SignatureKind::Ec(EcKind::P384));
206 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
207 }
208
209 #[test]
from_p521_pkey_not_supported()210 fn from_p521_pkey_not_supported() {
211 let pkey = load_public_pkey(testkeys::P521_KEY_PEM[0]);
212 assert!(PublicKey::try_from(pkey).is_err());
213 }
214
215 #[test]
from_rsa2048_pkey_not_supported()216 fn from_rsa2048_pkey_not_supported() {
217 let pkey = load_public_pkey(testkeys::RSA2048_KEY_PEM[0]);
218 assert!(PublicKey::try_from(pkey).is_err());
219 }
220
221 #[test]
from_x25519_pkey_not_supported()222 fn from_x25519_pkey_not_supported() {
223 let pkey = load_public_pkey(testkeys::X25519_KEY_PEM[0]);
224 assert!(PublicKey::try_from(pkey).is_err());
225 }
226
227 #[test]
key_agreement_key_from_x25519_pkey()228 fn key_agreement_key_from_x25519_pkey() {
229 let pkey = load_public_pkey(testkeys::X25519_KEY_PEM[0]);
230 let key: KeyAgreementPublicKey = pkey.clone().try_into().unwrap();
231 assert_eq!(key.kind, KeyAgreementKind::X25519);
232 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
233 }
234
235 #[test]
key_agreement_key_from_p256_pkey()236 fn key_agreement_key_from_p256_pkey() {
237 let pkey = load_public_pkey(testkeys::P256_KEY_PEM[0]);
238 let key: KeyAgreementPublicKey = pkey.clone().try_into().unwrap();
239 assert_eq!(key.kind, KeyAgreementKind::Ec(EcKind::P256));
240 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
241 }
242
243 #[test]
key_agreement_key_from_p384_pkey()244 fn key_agreement_key_from_p384_pkey() {
245 let pkey = load_public_pkey(testkeys::P384_KEY_PEM[0]);
246 let key: KeyAgreementPublicKey = pkey.clone().try_into().unwrap();
247 assert_eq!(key.kind, KeyAgreementKind::Ec(EcKind::P384));
248 assert_eq!(key.to_pem().as_bytes(), pkey.public_key_to_pem().unwrap());
249 }
250
251 #[test]
key_agreement_key_from_ed25519_pkey_not_supported()252 fn key_agreement_key_from_ed25519_pkey_not_supported() {
253 let pkey = load_public_pkey(testkeys::ED25519_KEY_PEM[0]);
254 assert!(KeyAgreementPublicKey::try_from(pkey).is_err());
255 }
256
load_public_pkey(pem: &str) -> PKey<Public>257 pub fn load_public_pkey(pem: &str) -> PKey<Public> {
258 testkeys::public_from_private(&PKey::private_key_from_pem(pem.as_bytes()).unwrap())
259 }
260
261 #[test]
verify_pkey_equality()262 fn verify_pkey_equality() {
263 let first = PKeyPublicWrapper(load_public_pkey(testkeys::ED25519_KEY_PEM[0]));
264 let second = PKeyPublicWrapper(load_public_pkey(testkeys::ED25519_KEY_PEM[0]));
265 assert_eq!(&first, &first);
266 assert_eq!(&first, &second);
267 assert_eq!(&second, &first);
268 }
269
270 #[test]
verify_key_kind_inequality()271 fn verify_key_kind_inequality() {
272 let ed25519 = PKeyPublicWrapper(load_public_pkey(testkeys::ED25519_KEY_PEM[0]));
273 let p256 = PKeyPublicWrapper(load_public_pkey(testkeys::P256_KEY_PEM[0]));
274 assert_ne!(&ed25519, &p256);
275 assert_ne!(&p256, &ed25519);
276 }
277
278 #[test]
verify_key_bits_inequality()279 fn verify_key_bits_inequality() {
280 let first = PKeyPublicWrapper(load_public_pkey(testkeys::P256_KEY_PEM[0]));
281 let second = PKeyPublicWrapper(load_public_pkey(testkeys::P256_KEY_PEM[1]));
282 assert_ne!(&first, &second);
283 assert_ne!(&second, &first);
284 }
285 }
286
287 /// Keys and key handling utilities for use in tests.
288 #[cfg(test)]
289 pub(crate) mod testkeys {
290 use super::*;
291 use openssl::pkey::Private;
292 use openssl::sign::Signer;
293
294 pub struct PrivateKey {
295 kind: SignatureKind,
296 pkey: PKey<Private>,
297 }
298
299 impl PrivateKey {
from_pem(pem: &str) -> Self300 pub fn from_pem(pem: &str) -> Self {
301 let pkey = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
302 let kind = PublicKey::pkey_kind(&pkey).expect("unsupported private key");
303 Self { kind, pkey }
304 }
305
kind(&self) -> SignatureKind306 pub(crate) fn kind(&self) -> SignatureKind {
307 self.kind
308 }
309
public_key(&self) -> PublicKey310 pub fn public_key(&self) -> PublicKey {
311 public_from_private(&self.pkey).try_into().unwrap()
312 }
313
sign(&self, message: &[u8]) -> Result<Vec<u8>>314 pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
315 let mut signer = match self.kind {
316 SignatureKind::Ed25519 => Signer::new_without_digest(&self.pkey)?,
317 SignatureKind::Ec(ec) => Signer::new(digest_for_ec(ec), &self.pkey)?,
318 };
319 signer.sign_oneshot_to_vec(message).context("signing message")
320 }
321 }
322
323 /// Gives the public key that matches the private key.
public_from_private(pkey: &PKey<Private>) -> PKey<Public>324 pub fn public_from_private(pkey: &PKey<Private>) -> PKey<Public> {
325 // It feels like there should be a more direct way to do this but I haven't found it.
326 PKey::public_key_from_der(&pkey.public_key_to_der().unwrap()).unwrap()
327 }
328
329 /// A selection of X25519 private keys.
330 pub const X25519_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
331 MC4CAQAwBQYDK2VuBCIEIMDLdDFad6CwwacwNtW/kQujlrAkxIjQ/Co3DleSd9xV\n\
332 -----END PRIVATE KEY-----\n"];
333
334 /// A selection of Ed25519 private keys.
335 pub const ED25519_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
336 MC4CAQAwBQYDK2VwBCIEILKW0KEeuieFxhDAzigQPE4XRTiQx+0/AlAjJqHmUWE6\n\
337 -----END PRIVATE KEY-----\n"];
338
339 pub const ED25519_KEY_WITH_LEADING_ZEROS_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
340 MC4CAQAwBQYDK2VwBCIEIBDTK4d0dffOye5RD6HsgcOFoDTtvQH1tPmr9RjpadxJ\n\
341 -----END PRIVATE KEY-----\n"];
342
343 /// A selection of elliptic curve P-256 private keys.
344 pub const P256_KEY_PEM: &[&str] = &[
345 "-----BEGIN PRIVATE KEY-----\n\
346 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+CO3ZBuAsimwPKAL\n\
347 IeDyCh4cRZ5EMd6llGu5MQCpibGhRANCAAQObPxc4bIPjupILrvKJjTrpTcyCf6q\n\
348 V552FlS67fGphwhg2LDfQ8adEdkuRfQvk+IvKJz8MDcPjErBG3Wlps1N\n\
349 -----END PRIVATE KEY-----\n",
350 "-----BEGIN PRIVATE KEY-----\n\
351 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgw1OPIcfQv5twO68B\n\
352 H+xNstW3DLXC6e4PGEYG/VppYVahRANCAAQMyWyv4ffVMu+wVNhNEk2mQSaTmSl/\n\
353 dLdRbEowfqPwMzdqdQ3QlKSV4ZcU2lsJEuQMkZzmVPz02enY2qcKctmj\n\
354 -----END PRIVATE KEY-----\n",
355 "-----BEGIN PRIVATE KEY-----\n\
356 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgbXO6ee7i7sY4YfFS\n\
357 Gn60ScPuL3QuYFMX4nJbcqPSQ7+hRANCAAS8i9xA8cIcWStbMG97YrttQsYEIR2a\n\
358 15+alxbb6b7422FuxBB0qG5nJ4m+Jd3Bp+N2lwx4rHBFDqU4cp8VlQav\n\
359 -----END PRIVATE KEY-----\n",
360 "-----BEGIN PRIVATE KEY-----\n\
361 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/JuxkbpPyyouat11\n\
362 szDR+OA7d/fuMk9IhGkH7z1xHzChRANCAASRlY0D7Uh5T/FmB6txGr21w6jqKW2x\n\
363 RXdsaZgCB6XnrXlkgkvuWDc0CTLSBWdPFgW6OX0fyXViglEBH95REyQr\n\
364 -----END PRIVATE KEY-----\n",
365 ];
366
367 /// A selection of EC keys that should have leading zeros in their coordinates
368 pub const EC2_KEY_WITH_LEADING_ZEROS_PEM: &[&str] = &[
369 // P256
370 // Public key has Y coordinate with most significant byte of 0x00
371 "-----BEGIN PRIVATE KEY-----\n\
372 MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCWbRSB3imI03F5YNVq\n\
373 8AN8ZbyzW/h+5BQ53caD5VkWJg==\n\
374 -----END PRIVATE KEY-----\n",
375 // P256
376 // Public key has X coordinate with most significant byte of 0x00
377 "-----BEGIN PRIVATE KEY-----\n\
378 MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDe5E5WqNmCLxtsCNTc\n\
379 UOb9CPXCn6l3CZpbrp0aivb+Bw==\n\
380 -----END PRIVATE KEY-----\n",
381 // P384
382 // Public key has Y coordinate with most significant byte of 0x00
383 "-----BEGIN PRIVATE KEY-----\n\
384 ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDCzgVHCz7wgmSdb7/IixYik\n\
385 3AuQceCtBTiFrJpgpGFluwgLUR0S2NpzIuty4M7xU74=\n\
386 -----END PRIVATE KEY-----\n",
387 // P384
388 // Public key has X coordinate with most significant byte of 0x00
389 "-----BEGIN PRIVATE KEY-----\n\
390 ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDBoW+8zbvwf5fYOS8YPyPEH\n\
391 jHP71Vr1MnRYRp/yG1wbthW2XEu0UWbp4qrZ5WTnZPg=\n\
392 -----END PRIVATE KEY-----\n",
393 ];
394 pub const EC2_KEY_WITH_HIGH_BITS_SET_PEM: &[&str] = &[
395 // P256
396 // Public key has X & Y coordinate that both have most significant bit set,
397 // and some stacks will add a padding byte
398 "-----BEGIN PRIVATE KEY-----\n\
399 MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCWOWcXPDEVZ4Qz3EBK\n\
400 uvSqhD9HmxDGxcNe3yxKi9pazw==\n\
401 -----END PRIVATE KEY-----\n",
402 // P384
403 // Public key has X & Y coordinate that both have most significant bit set,
404 // and some stacks will add a padding byte
405 "-----BEGIN PRIVATE KEY-----\n\
406 ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDD2A69j5M/6oc6/WGoYln4t\n\
407 Alnn0C6kpJz1EVC+eH6y0YNrcGamz8pPY4NkzUB/tj4=\n\
408 -----END PRIVATE KEY-----\n",
409 ];
410
411 /// A selection of elliptic curve P-384 private keys.
412 pub const P384_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
413 MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBMZ414LiUpcuNTNq5W\n\
414 Ig/qbnbFn0MpuZZxUn5YZ8/+2/tyXFFHRyQoQ4YpNN1P/+qhZANiAAScPDyisb21\n\
415 GldmGksI5g82hjPRYscWNs/6pFxQTMcxABE+/1lWaryLR193ZD74VxVRIKDBluRs\n\
416 uuHi+VayOreTX1/qlUoxgBT+XTI0nTdLn6WwO6vVO1NIkGEVnYvB2eM=\n\
417 -----END PRIVATE KEY-----\n"];
418
419 /// A selection of elliptic curve P-521 private keys.
420 pub const P521_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
421 MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBQuD8Db3jT2yPYR5t\n\
422 Y1ZqESxOe4eBWzekFKg7cjVWsSiJEvWTPC1H7CLtXQBHZglO90dwMt4flL91xHkl\n\
423 iZOzyHahgYkDgYYABAHACwmmKkZu01fp1QTTTQ0cv7IAfYv9FEBz8yfhNGPnI2WY\n\
424 iH1/lYeCfYc9d33aSc/ELY9+vIFzVStJumS/B/WTewEhxVomlKPAkUJeLdCaK5av\n\
425 nlUNj7pNQ/5v5FZVxmoFJvAtUAnZqnJqo/QkLtEnmKlzpLte2LuwTPZhG35z0HeL\n\
426 2g==\n\
427 -----END PRIVATE KEY-----\n"];
428
429 /// A selection of 2048-bit RSA private keys.
430 pub const RSA2048_KEY_PEM: &[&str] = &["-----BEGIN PRIVATE KEY-----\n\
431 MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbOJh7Ys7CuIju\n\
432 VVKMlFlZWwDEGBX5bVYD/xNBNNF1FY9bOcV/BG20IwoZkdV0N+vm8eWSuv/uwIJp\n\
433 sN2PMWPAEIWbPGGMnSdePpkwrdpFFywhEQqUrfdCFXZ8zeF85Nz5mL8ysl4vlMsL\n\
434 mbErCkrq++K0lzs+k7w/FtPCgs4M3WypJfZef5zM0CGWxpHZGoUGm0HW9fen4sv8\n\
435 hTmMGNY/r0SJhdZREGmiGCx2v+ksOEBon1r/6QKVTP8S73XFsyNCWyop0hYTakut\n\
436 D3HtJ5sWzu2RU8rrch3Txinz0jpGF8PATHk35YMw/9jwwwSqjDw+pOQcYk8SviAf\n\
437 glZf8aZlAgMBAAECggEAAS67PK67tuaOWywJSWHLsWGmqJ4I2tZiTzCT9EZ2MOVx\n\
438 +4ZChNfjHUsskXUp4XNL/FE0J3WvhEYjXR1u+L37nvqc48mJpjoPN7o/CMb6rM/J\n\
439 +ly9A2ZOvEB4ppOYDYh5QVDm7/otmvEMzJxuUOpvxYxqnJpAPgl9dBpNQ0nSt3YX\n\
440 jJS4+vuzQpwwSTfchpcCZYU3AX9DpQpxnrLX3/7d3GTs2NuedmSwRz+mCfwaOlFk\n\
441 jdrJ2uJJrDLcK6yhSdsE9aNgKkmX6aNLhxbbCFTyDNiGY5HHayyL3mVvyaeovYcn\n\
442 ZS+Z+0TJGCgXDRHHSFyIAsgVonxHfn49x9uvfpuMFQKBgQD2cVp26+aQgt46ajVv\n\
443 yn4fxbNpfovL0pgtSjo7ekZOWYJ3Is1SDmnni8k1ViKgUYC210dTTlrljxUil8T0\n\
444 83e03k2xasDi2c+h/7JFYJPDyZwIm1o19ciUwY73D54iJaRbrzEximFeA0h4LGKw\n\
445 Yjd4xkKMJw16CU00gInyI193BwKBgQDjuP0/QEEPpYvzpag5Ul4+h22K/tiOUrFj\n\
446 NuSgd+IvQG1hW48zHEa9vXvORQ/FteiQ708racz6ByqY+n2w6QdtdRMj7Hsyo2fk\n\
447 SEeNaLrR7Sif6MfkYajbSGFySDD82vj4Jt76vzdt3MjpZfs6ryPmnKLVPWNA3mnS\n\
448 4+u2J/+QMwKBgFfiJnugNnG0aaF1PKcoFAAqlYd6XEoMSL5l6QxK14WbP/5SR9wK\n\
449 TdQHsnI1zFVVm0wYy1O27o1MkCHs84zSwg6a9CPfyPdc60F/GMjK3wcD/4PGOs5h\n\
450 Xu1FdUE/rYnJ2KnleOqMyZooG5DXaz4xWEzWjubCCnlJleGyMP9LhADDAoGAR/jK\n\
451 iXgcV/6haeMcdOl0gdy5oWmENg8qo0nRHmplYTvCljei3at9LDC79WhcYMdqdow8\n\
452 AGOS9h7XtrvMh+JOh6it4Pe3xDxi9IJnoujLytditI+Uxbib7ppEuiLY4MGwWHWo\n\
453 maVftmhGU4X4zgZWmWc+C5k4SmNBHPcOI2cm3YMCgYB5/Ni+tBxng0S/PRAtwCeG\n\
454 dVnQnYvS2C5nHCn9D5rmAcVXUKrIJ1/1K4se8vQ15DDcpuBF1SejYTJzdUP8Zgcs\n\
455 p8wVq7neK8uSsmG+AfUgxMjbetoAVTP3L8+GbjocznR9auB7BEjFVO25iYSiTp/w\n\
456 NNzbIKQRDN+c3vUpneJcuw==\n\
457 -----END PRIVATE KEY-----\n"];
458 }
459