VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use