Migrates Neo KeyStoreService and PasswordStorage JNDI resources to SAP Credential Store using mTLS authentication and REST API. Detects references in web.xml and Java code.
npx claudepluginhub sap-samples/btp-neo-java-app-migration --plugin sap-btp-neo-migrationThis skill is limited to using the following tools:
Migrate keystore and password storage to SAP Credential Store.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Migrate keystore and password storage to SAP Credential Store.
Replace Neo's KeyStoreService and PasswordStorage JNDI resources with SAP Credential Store service using mTLS authentication and REST API.
This skill applies if any of these patterns are found:
<resource-ref>
<res-ref-name>KeyStoreService</res-ref-name>
<res-type>com.sap.cloud.crypto.keystore.api.KeyStoreService</res-type>
</resource-ref>
<!-- OR -->
<resource-ref>
<res-ref-name>PasswordStorage</res-ref-name>
<res-type>com.sap.cloud.security.password.PasswordStorage</res-type>
</resource-ref>
import com.sap.cloud.crypto.keystore.api.KeyStoreService;
import com.sap.cloud.security.password.PasswordStorage;
@Resource(name = "KeyStoreService")
private KeyStoreService keyStoreService;
@Resource(name = "PasswordStorage")
private PasswordStorage passwordStorage;
Working directory: This skill must run inside the
-cf-migrationcopy of your app, created byjakarta-java25-migrationorneo-to-cf-migration-orchestrator. If your current directory does not end in-cf-migration, switch to it before proceeding.
Before invoking this skill, ensure you have invoked:
Use the sdk-replacement skill
Also required:
Remove these from web.xml:
<resource-ref>
<res-ref-name>KeyStoreService</res-ref-name>
<res-type>com.sap.cloud.crypto.keystore.api.KeyStoreService</res-type>
</resource-ref>
<resource-ref>
<res-ref-name>PasswordStorage</res-ref-name>
<res-type>com.sap.cloud.security.password.PasswordStorage</res-type>
</resource-ref>
Copy the following helper classes to your project from assets/:
ServiceCredentialsAccessor.java - Reads Credential Store service bindingSSLContextProvider.java - Sets up mTLS authenticationCredStoreClient.java - Credential Store REST API clientThese classes handle:
Before (Neo - KeyStore):
import com.sap.cloud.crypto.keystore.api.KeyStoreService;
import java.security.KeyStore;
@Resource(name = "KeyStoreService")
private KeyStoreService keyStoreService;
public void useKeyStore() {
KeyStore keyStore = keyStoreService.getKeyStore();
PrivateKey key = (PrivateKey) keyStore.getKey("my-alias", password);
Certificate cert = keyStore.getCertificate("my-alias");
}
After (Cloud Foundry):
import com.example.credstore.client.CredStoreClient;
public class KeyStoreServlet extends HttpServlet {
private static final String NAMESPACE = "my-app-namespace";
private CredStoreClient credStoreClient;
@Override
public void init() throws ServletException {
try {
credStoreClient = new CredStoreClient();
} catch (Exception e) {
throw new ServletException("Failed to initialize CredStore client", e);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String alias = request.getParameter("alias");
try {
// Retrieve key from Credential Store
CredStoreClient.KeyCredential keyCredential =
credStoreClient.getKey(alias, NAMESPACE);
response.setContentType("application/json");
response.getWriter().printf(
"{\"name\": \"%s\", \"hasCertificate\": %b}",
keyCredential.getName(),
keyCredential.getCertificate() != null
);
} catch (Exception e) {
response.sendError(500, "Failed to retrieve key: " + e.getMessage());
}
}
}
Before (Neo - Password Storage):
import com.sap.cloud.security.password.PasswordStorage;
@Resource(name = "PasswordStorage")
private PasswordStorage passwordStorage;
public void usePassword() {
char[] password = passwordStorage.getPassword("my-password-alias");
// Use password...
Arrays.fill(password, '0'); // Clear password
}
After (Cloud Foundry):
import com.example.credstore.client.CredStoreClient;
public void usePassword() {
CredStoreClient client = new CredStoreClient();
String password = client.getPassword("my-password-alias", "my-namespace");
// Use password...
// Note: String passwords cannot be securely cleared in Java
}
modules:
- name: ${app-name}
type: java.tomcat
path: target/${app-name}.war
parameters:
buildpack: sap_java_buildpack_jakarta
disk-quota: 512MB
memory: 512MB
properties:
ENABLE_SECURITY_JAVA_API_V2: true
SET_LOGGING_LEVEL: 'ROOT: INFO'
requires:
- name: ${app-name}-credstore
resources:
- name: ${app-name}-credstore
type: org.cloudfoundry.managed-service
parameters:
service: credstore
service-plan: standard
config:
authentication:
type: mtls
Using BTP Cockpit or Credential Store API:
my-app-namespacedatabase-passwordsecret123my-certificateNo new configuration files required. Credentials are accessed via service binding.
| Service | Plan | Purpose |
|---|---|---|
credstore | standard | Secure credential storage |
mvn clean compile
cf env ${app-name} | grep -A 20 "credstore"
# Should show url, certificate, and key
curl "https://${app-url}/keystore?alias=my-certificate"
Cause: Service not bound to application. Solution: Check mtad.yaml requires section.
Cause: mTLS certificate parsing issue. Solution:
Cause: Invalid namespace or missing credential. Solution:
Cause: Credential doesn't exist in namespace. Solution: Create the credential in Credential Store dashboard.
After completing this skill, proceed to other applicable skills: