VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/RTCrStoreCertAddFromJavaKeyStore.cpp@ 67482

Last change on this file since 67482 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.2 KB
Line 
1/* $Id: RTCrStoreCertAddFromJavaKeyStore.cpp 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromJavaKeyStore.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_CRYPTO
32#include "internal/iprt.h"
33#include <iprt/crypto/store.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <iprt/file.h>
39#include <iprt/sha.h>
40#include <iprt/string.h>
41#include <iprt/log.h>
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47/** The java key store magic number (file endian). */
48#define JKS_MAGIC RT_H2BE_U32_C(UINT32_C(0xfeedfeed))
49/** Java key store format version 2 (file endian). */
50#define JKS_VERSION_2 RT_H2BE_U32_C(2)
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/**
57 * Java key store (JKS) header.
58 */
59typedef struct JKSHEADER
60{
61 /** The magic (big endian) - JKS_MAGIC. */
62 uint32_t uMagic;
63 /** Format version number (big endian) - JKS_VERSION_2. */
64 uint32_t uVersion;
65 /** The number of keystore entries (big endian). */
66 uint32_t cEntries;
67} JKSHEADER;
68/** Pointer to a const java key store header. */
69typedef JKSHEADER const *PCJKSHEADER;
70
71
72RTDECL(int) RTCrStoreCertAddFromJavaKeyStoreInMem(RTCRSTORE hStore, uint32_t fFlags, void const *pvContent, size_t cbContent,
73 const char *pszErrorName, PRTERRINFO pErrInfo)
74{
75 uint8_t const *pbContent = (uint8_t const *)pvContent;
76
77 /*
78 * Check the header.
79 */
80 if (cbContent < sizeof(JKSHEADER) + RTSHA1_HASH_SIZE)
81 return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
82 " Too small (%zu bytes) for java key store (%s)", cbContent, pszErrorName);
83 PCJKSHEADER pHdr = (PCJKSHEADER)pbContent;
84 if (pHdr->uMagic != JKS_MAGIC)
85 return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
86 " Not java key store magic %#x (%s)", RT_BE2H_U32(pHdr->uMagic), pszErrorName);
87 if (pHdr->uVersion != JKS_VERSION_2)
88 return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
89 " Unsupported java key store version %#x (%s)", RT_BE2H_U32(pHdr->uVersion), pszErrorName);
90 uint32_t const cEntries = RT_BE2H_U32(pHdr->cEntries);
91 if (cEntries > cbContent / 24) /* 24 = 4 for type, 4+ alias, 8 byte timestamp, 4 byte len, "X.509" or 4 cert count */
92 return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
93 " Entry count %u is to high for %zu byte JKS (%s)", cEntries, cbContent, pszErrorName);
94
95 /*
96 * Here we should check the store signature. However, it always includes
97 * some kind of password, and that's somewhere we don't want to go right
98 * now. Later perhaps.
99 *
100 * We subtract it from the content size to make EOF checks simpler.
101 */
102 int rc = VINF_SUCCESS;
103#if 0 /* later */
104 RTSHA1CONTEXT Ctx;
105 RTSha1Init(&Ctx);
106
107 const char *pszCur = pszPassword;
108 for (;;)
109 {
110 RTUNICP Cp;
111 rc = RTStrGetCpEx(&pszCur, &Cp);
112 AssertRCReturn(rc, rc);
113 if (!Cp)
114 break;
115 uint8_t abWChar[2];
116 abWChar[0] = RT_BYTE2(Cp);
117 abWChar[1] = RT_BYTE1(Cp);
118 RTSha1Update(&Ctx, &abWChar, sizeof(abWChar));
119 }
120
121 RTSha1Update(&Ctx, RT_STR_TUPLE("Mighty Aphrodite"));
122
123 RTSha1Update(&Ctx, pbContent, cbContent - RTSHA1_HASH_SIZE);
124
125 uint8_t abSignature[RTSHA1_HASH_SIZE];
126 RTSha1Final(&Ctx, abSignature);
127
128 if (memcmp(&pbContent[cbContent - RTSHA1_HASH_SIZE], abSignature, RTSHA1_HASH_SIZE) != 0)
129 {
130 rc = RTErrInfoAddF(pErrInfo, VERR_MISMATCH, " File SHA-1 signature mismatch, %.*Rhxs instead of %.*Rhxs, for '%s'",
131 RTSHA1_HASH_SIZE, abSignature,
132 RTSHA1_HASH_SIZE, &pbContent[cbContent - RTSHA1_HASH_SIZE],
133 pszErrorName);
134 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
135 return rc;
136 }
137#endif
138 cbContent -= RTSHA1_HASH_SIZE;
139
140
141 /*
142 * A bunch of macros to make decoding easier.
143 */
144#define ENSURE_CONTENT_OR_BREAK_EX(a_cbNeeded, a_pszWhat) \
145 do { \
146 if (RT_LIKELY(off + (a_cbNeeded) <= cbContent)) \
147 { /* likely */ } \
148 else \
149 { \
150 rc = RTErrInfoAddF(pErrInfo, VERR_EOF, " Unexpected end of data at %#x need %u bytes for %s (entry #%u in %s)", \
151 off, a_cbNeeded, a_pszWhat, iEntry, pszErrorName); \
152 break; \
153 } \
154 } while (0)
155#define ENSURE_CONTENT_OR_BREAK(a_Var) ENSURE_CONTENT_OR_BREAK_EX(sizeof(a_Var), #a_Var)
156#define GET_BE_U32_OR_BREAK(a_uVar) \
157 do { \
158 ENSURE_CONTENT_OR_BREAK(a_uVar); \
159 AssertCompile(sizeof(a_uVar) == sizeof(uint32_t)); \
160 a_uVar = RT_MAKE_U32_FROM_U8(pbContent[off + 3], pbContent[off + 2], pbContent[off + 1], pbContent[off + 0]); \
161 off += sizeof(uint32_t); \
162 } while (0)
163#define GET_BE_U16_OR_BREAK(a_uVar) \
164 do { \
165 ENSURE_CONTENT_OR_BREAK(a_uVar); \
166 AssertCompile(sizeof(a_uVar) == sizeof(uint16_t)); \
167 a_uVar = RT_MAKE_U16(pbContent[off + 1], pbContent[off + 0]); \
168 off += sizeof(uint16_t); \
169 } while (0)
170#define SKIP_CONTENT_BYTES_OR_BREAK(a_cbToSkip, a_pszWhat) \
171 do { \
172 ENSURE_CONTENT_OR_BREAK_EX(a_cbToSkip, a_pszWhat); \
173 off += a_cbToSkip; \
174 } while (0)
175#define CHECK_OR_BREAK(a_Expr, a_RTErrInfoAddFArgs) \
176 do { \
177 if (RT_LIKELY(a_Expr)) \
178 { /* likely */ } \
179 else \
180 { \
181 rc = RTErrInfoAddF a_RTErrInfoAddFArgs; \
182 break; \
183 } \
184 } while (0)
185
186 /*
187 * Work our way thru the keystore.
188 */
189 Log(("JKS: %u entries - '%s'\n", cEntries, pszErrorName));
190 size_t off = sizeof(JKSHEADER);
191 uint32_t iEntry = 0;
192 for (;;)
193 {
194 size_t const offEntry = off; NOREF(offEntry);
195
196 /* The entry type. */
197 uint32_t uType;
198 GET_BE_U32_OR_BREAK(uType);
199 CHECK_OR_BREAK(uType == 1 || uType == 2,
200 (pErrInfo, VERR_WRONG_TYPE, " uType=%#x (entry #%u in %s)", uType, iEntry, pszErrorName));
201
202 /* Skip the alias string. */
203 uint16_t cbAlias;
204 GET_BE_U16_OR_BREAK(cbAlias);
205 SKIP_CONTENT_BYTES_OR_BREAK(cbAlias, "szAlias");
206
207 /* Skip the creation timestamp. */
208 SKIP_CONTENT_BYTES_OR_BREAK(sizeof(uint64_t), "tsCreated");
209
210 uint32_t cTrustCerts = 0;
211 if (uType == 1)
212 {
213 /*
214 * It is a private key.
215 */
216 Log(("JKS: %#08zx: entry #%u: Private key\n", offEntry, iEntry));
217
218 /* The encoded key. */
219 uint32_t cbKey;
220 GET_BE_U32_OR_BREAK(cbKey);
221 SKIP_CONTENT_BYTES_OR_BREAK(cbKey, "key data");
222
223 /* The number of trust certificates following it. */
224 GET_BE_U32_OR_BREAK(cTrustCerts);
225 }
226 else if (uType == 2)
227 {
228 /*
229 * It is a certificate.
230 */
231 Log(("JKS: %#08zx: entry #%u: Trust certificate\n", offEntry, iEntry));
232 cTrustCerts = 1;
233 }
234 else
235 AssertFailedBreakStmt(rc = VERR_INTERNAL_ERROR_2);
236
237 /*
238 * Decode trust certificates. Keys have 0 or more of these associated with them.
239 */
240 for (uint32_t iCert = 0; iCert < cTrustCerts; iCert++)
241 {
242 /* X.509 signature */
243 static const char a_achCertType[] = { 0, 5, 'X', '.', '5', '0', '9' };
244 ENSURE_CONTENT_OR_BREAK(a_achCertType);
245 CHECK_OR_BREAK(memcmp(&pbContent[off], a_achCertType, sizeof(a_achCertType)) == 0,
246 (pErrInfo, VERR_WRONG_TYPE, " Unsupported certificate type %.7Rhxs (entry #%u in %s)",
247 &pbContent[off], iEntry, pszErrorName));
248 off += sizeof(a_achCertType);
249
250 /* The encoded certificate length. */
251 uint32_t cbEncoded;
252 GET_BE_U32_OR_BREAK(cbEncoded);
253 ENSURE_CONTENT_OR_BREAK_EX(cbEncoded, "certificate data");
254 Log(("JKS: %#08zx: %#x certificate bytes\n", off, cbEncoded));
255
256 /* Try add the certificate. */
257 RTERRINFOSTATIC StaticErrInfo;
258 int rc2 = RTCrStoreCertAddEncoded(hStore,
259 RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
260 &pbContent[off], cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
261 if (RT_FAILURE(rc2))
262 {
263 if (RTErrInfoIsSet(&StaticErrInfo.Core))
264 rc = RTErrInfoAddF(pErrInfo, rc2, " entry #%u: %s", iEntry, StaticErrInfo.Core.pszMsg);
265 else
266 rc = RTErrInfoAddF(pErrInfo, rc2, " entry #%u: %Rrc adding cert", iEntry, rc2);
267 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
268 break;
269 }
270 off += cbEncoded;
271 }
272
273 /*
274 * Advance.
275 */
276 iEntry++;
277 if (iEntry >= cEntries)
278 {
279 if (off != cbContent)
280 rc = RTErrInfoAddF(pErrInfo, VERR_TOO_MUCH_DATA, " %zu tailing bytes (%s)", cbContent - off, pszErrorName);
281 break;
282 }
283 }
284
285 return rc;
286}
287
288
289RTDECL(int) RTCrStoreCertAddFromJavaKeyStore(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
290{
291 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
292
293 /*
294 * Read the whole thing into memory as that's much more convenient to work
295 * with and we don't expect a java key store to take up a lot of space.
296 */
297 size_t cbContent;
298 void *pvContent;
299 int rc = RTFileReadAllEx(pszFilename, 0, 32U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
300 if (RT_SUCCESS(rc))
301 {
302 rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
303 RTFileReadAllFree(pvContent, cbContent);
304 }
305 else
306 rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
307 return rc;
308}
309RT_EXPORT_SYMBOL(RTCrStoreCertAddFromJavaKeyStore);
310
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette