• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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