1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 /*
21 * XSEC
22 *
23 * WinCAPICryptoKeyDSA := DSA Keys
24 *
25 * Author(s): Berin Lautenbach
26 *
27 * $Id: WinCAPICryptoKeyDSA.cpp 1817863 2017-12-11 22:47:43Z scantor $
28 *
29 */
30
31 #include <xsec/enc/WinCAPI/WinCAPICryptoKeyDSA.hpp>
32 #include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
33 #include <xsec/enc/XSCrypt/XSCryptCryptoBase64.hpp>
34 #include <xsec/enc/XSECCryptoException.hpp>
35 #include <xsec/enc/XSECCryptoUtils.hpp>
36 #include <xsec/framework/XSECError.hpp>
37
38 #if defined (XSEC_HAVE_WINCAPI)
39
40 #include <xercesc/util/Janitor.hpp>
41
42 XSEC_USING_XERCES(ArrayJanitor);
43
WinCAPICryptoKeyDSA(HCRYPTPROV prov)44 WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov) {
45
46 // Create a new key to be loaded as we go
47
48 m_key = 0;
49 m_p = prov;
50 m_keySpec = 0;
51
52 mp_P = NULL;
53 mp_Q = NULL;
54 mp_G = NULL;
55 mp_Y = NULL;
56
57 };
58
59 // "Hidden" WinCAPI constructor
60
WinCAPICryptoKeyDSA(HCRYPTPROV prov,HCRYPTKEY k)61 WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov,
62 HCRYPTKEY k) :
63 m_p(prov) {
64
65 m_key = k; // NOTE - We OWN this handle
66 m_keySpec = 0;
67
68 mp_P = mp_Q = mp_G = mp_Y = NULL;
69 m_PLen = m_QLen = m_GLen = m_YLen = 0;
70
71 }
72
WinCAPICryptoKeyDSA(HCRYPTPROV prov,DWORD keySpec,bool isPrivate)73 WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov,
74 DWORD keySpec,
75 bool isPrivate) :
76 m_p(prov) {
77
78 if (isPrivate == false) {
79
80 throw XSECCryptoException(XSECCryptoException::DSAError,
81 "Public keys defined via keySpec ctor not yet supported");
82
83
84 }
85
86 m_key = 0;
87 m_keySpec = keySpec;
88
89 mp_P = mp_Q = mp_G = mp_Y = NULL;
90 m_PLen = m_QLen = m_GLen = m_YLen = 0;
91
92 }
93
~WinCAPICryptoKeyDSA()94 WinCAPICryptoKeyDSA::~WinCAPICryptoKeyDSA() {
95
96
97 // If we have a DSA, delete it
98
99 if (m_key != 0)
100 CryptDestroyKey(m_key);
101
102 if (mp_P != NULL)
103 delete[] mp_P;
104 if (mp_Q != NULL)
105 delete[] mp_Q;
106 if (mp_G != NULL)
107 delete[] mp_G;
108 if (mp_Y != NULL)
109 delete[] mp_Y;
110
111 };
112
getProviderName() const113 const XMLCh * WinCAPICryptoKeyDSA::getProviderName() const {
114 return DSIGConstants::s_unicodeStrPROVWinCAPI;
115 }
116
117 // Generic key functions
118
getKeyType() const119 XSECCryptoKey::KeyType WinCAPICryptoKeyDSA::getKeyType() const {
120
121 // Find out what we have
122 if (m_key == NULL) {
123
124 // For now we don't really understand Private Windows keys
125 if (m_keySpec != 0)
126 return KEY_DSA_PRIVATE;
127
128 // Check if we have parameters loaded
129 if (mp_P == NULL ||
130 mp_Q == NULL ||
131 mp_G == NULL ||
132 mp_Y == NULL)
133 return KEY_NONE;
134 else
135 return KEY_DSA_PUBLIC;
136 }
137
138 if (m_keySpec != 0)
139 return KEY_DSA_PAIR;
140
141 // If we have m_key - it must be public
142
143 return KEY_DSA_PUBLIC;
144
145 }
146
loadPBase64BigNums(const char * b64,unsigned int len)147 void WinCAPICryptoKeyDSA::loadPBase64BigNums(const char * b64, unsigned int len) {
148
149 if (mp_P != NULL) {
150 delete[] mp_P;
151 mp_P = NULL; // In case we get an exception
152 }
153
154 mp_P = WinCAPICryptoProvider::b642WinBN(b64, len, m_PLen);
155
156 }
157
loadQBase64BigNums(const char * b64,unsigned int len)158 void WinCAPICryptoKeyDSA::loadQBase64BigNums(const char * b64, unsigned int len) {
159
160 if (mp_Q != NULL) {
161 delete[] mp_Q;
162 mp_Q = NULL; // In case we get an exception
163 }
164
165 mp_Q = WinCAPICryptoProvider::b642WinBN(b64, len, m_QLen);
166 }
167
loadGBase64BigNums(const char * b64,unsigned int len)168 void WinCAPICryptoKeyDSA::loadGBase64BigNums(const char * b64, unsigned int len) {
169
170 if (mp_G != NULL) {
171 delete[] mp_G;
172 mp_G = NULL; // In case we get an exception
173 }
174
175 mp_G = WinCAPICryptoProvider::b642WinBN(b64, len, m_GLen);
176 }
177
loadYBase64BigNums(const char * b64,unsigned int len)178 void WinCAPICryptoKeyDSA::loadYBase64BigNums(const char * b64, unsigned int len) {
179
180 if (mp_Y != NULL) {
181 delete[] mp_Y;
182 mp_Y = NULL; // In case we get an exception
183 }
184
185 mp_Y = WinCAPICryptoProvider::b642WinBN(b64, len, m_YLen);
186 }
187
loadJBase64BigNums(const char * b64,unsigned int len)188 void WinCAPICryptoKeyDSA::loadJBase64BigNums(const char * b64, unsigned int len) {
189 /*
190 Do nothing
191 */
192 }
193
194
195 // --------------------------------------------------------------------------------
196 // Verify a signature encoded as a Base64 string
197 // --------------------------------------------------------------------------------
198
importKey(void) const199 void WinCAPICryptoKeyDSA::importKey(void) const {
200
201 if (m_key != 0 ||
202 mp_P == NULL ||
203 mp_Q == NULL ||
204 mp_G == NULL ||
205 mp_Y == NULL)
206 return;
207
208 // Do some basic checks
209 if ((m_QLen > 20) |
210 (m_GLen > m_PLen) |
211 (m_YLen > m_PLen)) {
212
213 throw XSECCryptoException(XSECCryptoException::DSAError,
214 "WinCAPI:DSA - Parameter lengths inconsistent");
215 }
216
217 // Create a DSS Public-Key blob
218
219 // First build a buffer to hold everything
220 BYTE * blobBuffer;
221 unsigned int blobBufferLen = WINCAPI_BLOBHEADERLEN + WINCAPI_DSSPUBKEYLEN + (3 * m_PLen) + 0x14 + WINCAPI_DSSSEEDLEN;
222 XSECnew(blobBuffer, BYTE[blobBufferLen]);
223 ArrayJanitor<BYTE> j_blobBuffer(blobBuffer);
224
225 // Blob header
226 BLOBHEADER * header = (BLOBHEADER *) blobBuffer;
227
228 header->bType = PUBLICKEYBLOB;
229 header->bVersion = 0x02; // We are using a version 2 blob
230 header->reserved = 0;
231 header->aiKeyAlg = CALG_DSS_SIGN;
232
233 // Now the public key header
234 DSSPUBKEY * pubkey = (DSSPUBKEY *) (blobBuffer + WINCAPI_BLOBHEADERLEN);
235
236 pubkey->magic = 0x31535344; // ASCII encoding of DSS1
237 pubkey->bitlen = m_PLen * 8; // Number of bits in prime modulus
238
239 // Now copy in each of the keys
240 BYTE * i = (BYTE *) (pubkey);
241 i += WINCAPI_DSSPUBKEYLEN;
242
243 memcpy(i, mp_P, m_PLen);
244 i+= m_PLen;
245
246 // Q
247 memcpy(i, mp_Q, m_QLen);
248 i += m_QLen;
249
250 // Pad with 0s
251 unsigned int j;
252 for (j = m_QLen; j < 20 ; ++j)
253 *i++ = 0;
254
255 // Generator
256 memcpy(i, mp_G, m_GLen);
257 i += m_GLen;
258 // Pad
259 for (j = m_GLen; j < m_PLen ; ++j)
260 *i++ = 0;
261
262 // Public key
263 memcpy(i, mp_Y, m_YLen);
264 i += m_YLen;
265 // Pad
266 for (j = m_YLen; j < m_PLen ; ++j)
267 *i++ = 0;
268
269 // Set seed to 0
270 for (j = 0; j < WINCAPI_DSSSEEDLEN; ++j)
271 *i++ = 0xFF; // SEED Counter set to 0xFFFFFFFF will cause seed to be ignored
272
273 // Now that we have the blob, import
274 BOOL fResult = CryptImportKey(
275 m_p,
276 blobBuffer,
277 blobBufferLen,
278 0, // Not signed
279 0, // No flags
280 &m_key);
281
282 if (fResult == 0) {
283
284 throw XSECCryptoException(XSECCryptoException::DSAError,
285 "WinCAPI:DSA Error attempting to import key parameters");
286
287 }
288
289 }
290
verifyBase64Signature(unsigned char * hashBuf,unsigned int hashLen,char * base64Signature,unsigned int sigLen) const291 bool WinCAPICryptoKeyDSA::verifyBase64Signature(unsigned char * hashBuf,
292 unsigned int hashLen,
293 char * base64Signature,
294 unsigned int sigLen) const {
295
296 // Use the currently loaded key to validate the Base64 encoded signature
297
298 if (m_key == 0) {
299
300 // Try to import from the parameters
301 importKey();
302
303 if (m_key == 0) {
304 throw XSECCryptoException(XSECCryptoException::DSAError,
305 "WinCAPI:DSA - Attempt to validate signature with empty key");
306 }
307 }
308
309 // Decode the signature
310 unsigned char * rawSig;
311 DWORD rawSigLen;
312 XSECnew(rawSig, BYTE [sigLen]);
313 ArrayJanitor<BYTE> j_rawSig(rawSig);
314
315 // Decode the signature
316 XSCryptCryptoBase64 b64;
317
318 b64.decodeInit();
319 rawSigLen = b64.decode((unsigned char *) base64Signature, sigLen, rawSig, sigLen);
320 rawSigLen += b64.decodeFinish(&rawSig[rawSigLen], sigLen - rawSigLen);
321
322 // Reverse the sig - Windows stores integers as octet streams in little endian
323 // order. The I2OSP algorithm used by XMLDSig to store integers is big endian
324
325 BYTE rawSigFinal[40];
326 BYTE * j, *k, *l, *m;
327
328 unsigned char rb[20];
329 unsigned char sb[20];
330
331 if (rawSigLen == 40) {
332
333 j = rawSig;
334 k = rawSig + 20;
335
336 } else if (rawSigLen == 46 && ASN2DSASig(rawSig, rb, sb) == true) {
337
338 j = rb;
339 k = sb;
340
341 } else {
342
343 throw XSECCryptoException(XSECCryptoException::DSAError,
344 "WinCAPI:DSA::VerifyBase64Signature - Expect 40 bytes in a DSA signature");
345 }
346
347 l = rawSigFinal + 19;
348 m = rawSigFinal + 39;
349
350 while (l >= rawSigFinal) {
351 *l-- = *j++;
352 *m-- = *k++;
353 }
354
355 // Have to create a Windows hash object and feed in the hash
356 BOOL fResult;
357 HCRYPTHASH h;
358 fResult = CryptCreateHash(m_p,
359 CALG_SHA1,
360 0,
361 0,
362 &h);
363
364 if (!fResult) {
365 throw XSECCryptoException(XSECCryptoException::DSAError,
366 "WinCAPI:DSA - Error creating Windows Hash Object");
367 }
368
369 // Feed the hash value into the newly created hash object
370 fResult = CryptSetHashParam(
371 h,
372 HP_HASHVAL,
373 hashBuf,
374 0);
375
376 if (!fResult) {
377 if (h)
378 CryptDestroyHash(h);
379 throw XSECCryptoException(XSECCryptoException::DSAError,
380 "WinCAPI:DSA - Error Setting Hash Value in Windows Hash object");
381 }
382
383 // Now validate
384 fResult = CryptVerifySignature(
385 h,
386 rawSigFinal,
387 40,
388 m_key,
389 NULL,
390 0);
391
392 if (!fResult) {
393
394 DWORD error = GetLastError();
395
396 /* For some reason, the default Microsoft DSS provider generally returns
397 * NTE_FAIL (which denotes an internal failure in the provider) for a
398 * failed signature rather than NTE_BAD_SIGNATURE
399 */
400
401 if (error != NTE_BAD_SIGNATURE && error != NTE_FAIL) {
402 if (h)
403 CryptDestroyHash(h);
404 throw XSECCryptoException(XSECCryptoException::DSAError,
405 "WinCAPI:DSA - Error occurred in DSA validation");
406 }
407
408 if (h)
409 CryptDestroyHash(h);
410 return false;
411 }
412
413 if (h)
414 CryptDestroyHash(h);
415
416 return true;
417
418 }
419
420 // --------------------------------------------------------------------------------
421 // Sign and encode result as a Base64 string
422 // --------------------------------------------------------------------------------
423
424
signBase64Signature(unsigned char * hashBuf,unsigned int hashLen,char * base64SignatureBuf,unsigned int base64SignatureBufLen) const425 unsigned int WinCAPICryptoKeyDSA::signBase64Signature(unsigned char * hashBuf,
426 unsigned int hashLen,
427 char * base64SignatureBuf,
428 unsigned int base64SignatureBufLen) const {
429
430 // Sign a pre-calculated hash using this key
431
432 if (m_keySpec == 0) {
433
434 throw XSECCryptoException(XSECCryptoException::DSAError,
435 "WinCAPI:DSA - Attempt to sign data a public or non-existent key");
436 }
437
438 // Have to create a Windows hash object and feed in the hash
439 BOOL fResult;
440 HCRYPTHASH h;
441 fResult = CryptCreateHash(m_p,
442 CALG_SHA1,
443 0,
444 0,
445 &h);
446
447 if (!fResult) {
448 throw XSECCryptoException(XSECCryptoException::DSAError,
449 "WinCAPI:DSA - Error creating Windows Hash Object");
450 }
451
452 // Feed the hash value into the newly created hash object
453 fResult = CryptSetHashParam(
454 h,
455 HP_HASHVAL,
456 hashBuf,
457 0);
458
459 if (!fResult) {
460 if (h)
461 CryptDestroyHash(h);
462 throw XSECCryptoException(XSECCryptoException::DSAError,
463 "WinCAPI:DSA - Error Setting Hash Value in Windows Hash object");
464 }
465
466 // Now sign
467 BYTE rawSig[50];
468 DWORD rawSigLen = 50;
469 fResult = CryptSignHash(
470 h,
471 m_keySpec,
472 NULL,
473 0,
474 rawSig,
475 &rawSigLen);
476
477 if (!fResult || rawSigLen != 40) {
478
479 if (h)
480 CryptDestroyHash(h);
481 throw XSECCryptoException(XSECCryptoException::DSAError,
482 "WinCAPI:DSA - Error occurred in DSA signing");
483 }
484
485 if (h)
486 CryptDestroyHash(h);
487 // Now encode into a signature block
488 BYTE rawSigFinal[40];
489
490 BYTE * i, * j, * m, * n;
491
492 i = rawSig;
493 j = rawSig + 20;
494 m = rawSigFinal + 19;
495 n = rawSigFinal + 39;
496
497 while (m >= rawSigFinal) {
498 *m-- = *i++;
499 *n-- = *j++;
500 }
501
502 // Now encode
503 XSCryptCryptoBase64 b64;
504 b64.encodeInit();
505 unsigned int ret = b64.encode(rawSigFinal, 40, (unsigned char *) base64SignatureBuf, base64SignatureBufLen);
506 ret += b64.encodeFinish((unsigned char *) &base64SignatureBuf[ret], base64SignatureBufLen - ret);
507
508 return ret;
509
510 }
511
512 // --------------------------------------------------------------------------------
513 // Clone key
514 // --------------------------------------------------------------------------------
515
516
clone() const517 XSECCryptoKey * WinCAPICryptoKeyDSA::clone() const {
518
519 WinCAPICryptoKeyDSA * ret;
520
521 XSECnew(ret, WinCAPICryptoKeyDSA(m_p));
522
523 if (m_key != 0) {
524
525 // CryptDuplicateKey is not supported in Windows NT, so we need to export and then
526 // reimport the key to get a copy
527
528 BYTE keyBuf[2048];
529 DWORD keyBufLen = 2048;
530 CryptExportKey(m_key, 0, PUBLICKEYBLOB, 0, keyBuf, &keyBufLen);
531
532 // Now re-import
533 CryptImportKey(m_p, keyBuf, keyBufLen, NULL, 0, &ret->m_key);
534 }
535
536 ret->m_PLen = m_PLen;
537 if (mp_P != NULL) {
538 XSECnew(ret->mp_P, BYTE[m_PLen]);
539 memcpy(ret->mp_P, mp_P, m_PLen);
540 }
541 else
542 ret->mp_P = NULL;
543
544 ret->m_QLen = m_QLen;
545 if (mp_Q != NULL) {
546 XSECnew(ret->mp_Q, BYTE[m_QLen]);
547 memcpy(ret->mp_Q, mp_Q, m_QLen);
548 }
549 else
550 ret->mp_Q = NULL;
551
552 ret->m_GLen = m_GLen;
553 if (mp_G != NULL) {
554 XSECnew(ret->mp_G, BYTE[m_GLen]);
555 memcpy(ret->mp_G, mp_G, m_GLen);
556 }
557 else
558 ret->mp_G = NULL;
559
560 ret->m_YLen = m_YLen;
561 if (mp_Y != NULL) {
562 XSECnew(ret->mp_Y, BYTE[m_YLen]);
563 memcpy(ret->mp_Y, mp_Y, m_YLen);
564 }
565 else
566 ret->mp_Y = NULL;
567
568
569
570 return ret;
571
572 }
573
574 // --------------------------------------------------------------------------------
575 // Some utility functions
576 // --------------------------------------------------------------------------------
577
loadParamsFromKey(void)578 void WinCAPICryptoKeyDSA::loadParamsFromKey(void) {
579
580 if (m_key == 0) {
581
582 if (m_keySpec == 0)
583 return;
584
585 // See of we can get the user key
586 if (!CryptGetUserKey(m_p, m_keySpec, &m_key))
587 return;
588
589 }
590
591 // Export key into a keyblob
592 BOOL fResult;
593 DWORD blobLen;
594
595 fResult = CryptExportKey(
596 m_key,
597 0,
598 PUBLICKEYBLOB,
599 0,
600 NULL,
601 &blobLen);
602
603 if (fResult == 0 || blobLen < 1) {
604 throw XSECCryptoException(XSECCryptoException::DSAError,
605 "WinCAPI:DSA - Error exporting public key");
606 }
607
608 BYTE * blob;
609 XSECnew(blob, BYTE[blobLen]);
610 ArrayJanitor<BYTE> j_blob(blob);
611
612 fResult = CryptExportKey(
613 m_key,
614 0,
615 PUBLICKEYBLOB,
616 0,
617 blob,
618 &blobLen);
619
620 if (fResult == 0 || blobLen < 1) {
621 throw XSECCryptoException(XSECCryptoException::DSAError,
622 "WinCAPI:DSA - Error exporting public key");
623 }
624
625 DSSPUBKEY * pk = (DSSPUBKEY *) ( blob + WINCAPI_BLOBHEADERLEN );
626 DWORD keyLen = pk->bitlen / 8;
627
628 // Copy the keys
629
630 BYTE * i = (BYTE *) ( pk );
631 i += WINCAPI_DSSPUBKEYLEN;
632 if (mp_P != NULL)
633 delete[] mp_P;
634
635 XSECnew(mp_P, BYTE[keyLen]);
636 memcpy(mp_P, i, keyLen);
637 m_PLen = keyLen;
638
639 i+=keyLen;
640
641 if (mp_Q != NULL)
642 delete[] mp_Q;
643
644 m_QLen = 20;
645 while (i[m_QLen - 1] == 0 && m_QLen > 0)
646 m_QLen--;
647 XSECnew(mp_Q, BYTE[m_QLen]);
648 memcpy(mp_Q, i, m_QLen);
649
650 i+=20;
651
652 if (mp_G != NULL)
653 delete[] mp_G;
654
655 m_GLen = keyLen;
656 while(i[m_GLen - 1] == 0 && m_GLen > 0)
657 m_GLen--;
658
659 XSECnew(mp_G, BYTE[m_GLen]);
660 memcpy(mp_G, i, m_GLen);
661
662 i+=keyLen;
663
664 if (mp_Y != NULL)
665 delete[] mp_Y;
666
667 m_YLen = keyLen;
668 while (i[m_YLen] == 0 && m_YLen > 0)
669 m_YLen--;
670
671 XSECnew(mp_Y, BYTE[m_YLen]);
672 memcpy(mp_Y, i, m_YLen);
673
674 }
675
getPBase64BigNums(char * b64,unsigned int len)676 unsigned int WinCAPICryptoKeyDSA::getPBase64BigNums(char * b64, unsigned int len) {
677
678 if (m_key == 0 && m_keySpec == 0 && mp_P == NULL) {
679
680 return 0; // Nothing we can do
681
682 }
683
684 if (mp_P == NULL) {
685
686 loadParamsFromKey();
687
688 }
689
690 unsigned int bLen;
691 unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_P, m_PLen, bLen);
692 if (bLen > len)
693 bLen = len;
694 memcpy(b64, b, bLen);
695 delete[] b;
696
697 return bLen;
698
699 }
700
getQBase64BigNums(char * b64,unsigned int len)701 unsigned int WinCAPICryptoKeyDSA::getQBase64BigNums(char * b64, unsigned int len) {
702
703 if (m_key == 0 && m_keySpec == 0 && mp_Q == NULL) {
704
705 return 0; // Nothing we can do
706
707 }
708
709 if (mp_Q == NULL) {
710
711 loadParamsFromKey();
712
713 }
714
715 unsigned int bLen;
716 unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_Q, m_QLen, bLen);
717 if (bLen > len)
718 bLen = len;
719 memcpy(b64, b, bLen);
720 delete[] b;
721
722 return bLen;
723
724 }
725
getGBase64BigNums(char * b64,unsigned int len)726 unsigned int WinCAPICryptoKeyDSA::getGBase64BigNums(char * b64, unsigned int len) {
727
728 if (m_key == 0 && m_keySpec == 0 && mp_G == NULL) {
729
730 return 0; // Nothing we can do
731
732 }
733
734 if (mp_G == NULL) {
735
736 loadParamsFromKey();
737
738 }
739
740 unsigned int bLen;
741 unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_G, m_GLen, bLen);
742 if (bLen > len)
743 bLen = len;
744 memcpy(b64, b, bLen);
745 delete[] b;
746
747 return bLen;
748
749 }
750
getYBase64BigNums(char * b64,unsigned int len)751 unsigned int WinCAPICryptoKeyDSA::getYBase64BigNums(char * b64, unsigned int len) {
752
753 if (m_key == 0 && m_keySpec == 0 && mp_Y == NULL) {
754
755 return 0; // Nothing we can do
756
757 }
758
759 if (mp_Y == NULL) {
760
761 loadParamsFromKey();
762
763 }
764
765 unsigned int bLen;
766 unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_Y, m_YLen, bLen);
767 if (bLen > len)
768 bLen = len;
769 memcpy(b64, b, bLen);
770 delete[] b;
771
772 return bLen;
773
774 }
775
776 #endif /* XSEC_HAVE_WINCAPI */
777
778