├── img ├── README.md ├── updater-1.png ├── updater-2.png ├── updater-3.png ├── updater-4.png ├── installer-1.png └── installer-2.png ├── .github ├── FUNDING.yml └── workflows │ └── reuse.yml ├── REUSE.toml ├── src ├── package.devc.xml ├── #apmg#.nspc.xml ├── #apmg#cl_strust_cert_api.clas.xml ├── #apmg#cl_strust.clas.xml ├── #apmg#strust_log.tabl.xml ├── #apmg#strust_installer.prog.xml ├── #apmg#strust_updater.prog.xml ├── #apmg#cl_strust_cert_api.clas.abap ├── #apmg#strust_installer.prog.abap ├── #apmg#strust_updater.prog.abap └── #apmg#cl_strust.clas.abap ├── .gitattributes ├── .abapgit.xml ├── .editorconfig ├── package.abap.json ├── LICENSE ├── LICENSES ├── MIT.txt └── CC0-1.0.txt ├── CONTRIBUTING.md ├── README.md └── abaplint.json /img/README.md: -------------------------------------------------------------------------------- 1 | Screen shots 2 | -------------------------------------------------------------------------------- /img/updater-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/updater-1.png -------------------------------------------------------------------------------- /img/updater-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/updater-2.png -------------------------------------------------------------------------------- /img/updater-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/updater-3.png -------------------------------------------------------------------------------- /img/updater-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/updater-4.png -------------------------------------------------------------------------------- /img/installer-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/installer-1.png -------------------------------------------------------------------------------- /img/installer-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abapPM/ABAP-Strust/HEAD/img/installer-2.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # All contributions are important small and big 2 | github: marc-bernard-tools 3 | github: mbtools 4 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[annotations]] 4 | path = "**/*" 5 | SPDX-FileCopyrightText = "2025 apm.to Inc. " 6 | SPDX-License-Identifier = "MIT" 7 | -------------------------------------------------------------------------------- /src/package.devc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Trust Management 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # newlines 2 | * text=auto eol=lf 3 | 4 | *.xml text eol=lf 5 | *.abap text eol=lf 6 | *.md text eol=lf 7 | *.txt text eol=lf 8 | *.yml text eol=lf 9 | *.yaml text eol=lf 10 | *.js text eol=lf 11 | *.json text eol=lf 12 | *.html text eol=lf 13 | *.css text eol=lf 14 | *.svg text eol=lf 15 | *.sh text eol=lf 16 | 17 | *.jpg binary 18 | *.gif binary 19 | *.png binary 20 | *.jpeg binary 21 | -------------------------------------------------------------------------------- /src/#apmg#.nspc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/ 7 | 41813564412598342476 8 | 9 | 10 | E 11 | apm 12 | apm.to Inc. 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/reuse.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | name: REUSE Compliance Check 6 | 7 | on: [push, pull_request, workflow_dispatch] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | Reuse-Compliance-Check: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: REUSE Compliance Check 20 | uses: fsfe/reuse-action@694eabb15673ec601ea19d4cd949969141372512 # v5.0.0 21 | -------------------------------------------------------------------------------- /src/#apmg#cl_strust_cert_api.clas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/CL_STRUST_CERT_API 7 | E 8 | Trust Management: Certificate API 9 | 1 10 | X 11 | X 12 | X 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.abapgit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | trust-management 6 | E 7 | /src/ 8 | PREFIX 9 | 10 | 11 | SAP_BASIS 12 | 750 13 | 14 | 15 | /APMG/CL_STRUST=>C_VERSION 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # 1 space indentation for xml files 7 | [*.xml] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 1 14 | 15 | # match the format used by abapGit 16 | [*.{abap,js,json,html,css}] 17 | charset = utf-8 18 | end_of_line = lf 19 | insert_final_newline = true 20 | trim_trailing_whitespace = true 21 | indent_size = 2 22 | indent_style = space 23 | 24 | # YAML and Bash Files 25 | [*.{yml,yaml,sh}] 26 | charset = utf-8 27 | end_of_line = lf 28 | insert_final_newline = true 29 | trim_trailing_whitespace = true 30 | indent_size = 2 31 | indent_style = space 32 | -------------------------------------------------------------------------------- /package.abap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strust", 3 | "version": "2.1.1", 4 | "description": "Add, update, or remove certificates from ABAP Trust Management", 5 | "type": "module", 6 | "keywords": [ 7 | "abap", 8 | "apm", 9 | "module", 10 | "strust" 11 | ], 12 | "homepage": "https://abappm.com", 13 | "bugs": { 14 | "url": "https://github.com/abapPM/ABAP-Strust" 15 | }, 16 | "license": "MIT", 17 | "author": { 18 | "name": "Marc Bernard", 19 | "url": "https://abappm.com", 20 | "email": "marc@abappm.com" 21 | }, 22 | "maintainers": [ 23 | { 24 | "name": "Marc Bernard", 25 | "url": "https://github.com/mbtools", 26 | "email": "marc@abappm.com" 27 | } 28 | ], 29 | "repository": { 30 | "url": "https://github.com/abapPM/ABAP-Strust" 31 | }, 32 | "funding": { 33 | "type": "github", 34 | "url": "https://github.com/sponsors/abapPM" 35 | }, 36 | "dependencies": { 37 | "ajson": "^1.1.12", 38 | "distinguished-name": "^1.0.0", 39 | "error": "^1.0.0" 40 | }, 41 | "engines": { 42 | "abap": ">=7.50", 43 | "apm": ">=1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2023 apm.to Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2023 Marc Bernard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/#apmg#cl_strust.clas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/CL_STRUST 7 | E 8 | Trust Management 9 | 1 10 | X 11 | X 12 | X 13 | 14 | 15 | 16 | I 17 | 010 18 | Inconsistent certificate format 19 | 80 20 | 21 | 22 | I 23 | 011 24 | Missing profile. Call "load" first 25 | 80 26 | 27 | 28 | I 29 | 012 30 | Error saving comments to log table 31 | 80 32 | 33 | 34 | I 35 | 020 36 | Error deleting file 37 | 80 38 | 39 | 40 | I 41 | 030 42 | Not authorized to delete file 43 | 80 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/#apmg#strust_log.tabl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/STRUST_LOG 7 | E 8 | TRANSP 9 | Trust Management: Log 10 | A 11 | 1 12 | 13 | 14 | /APMG/STRUST_LOG 15 | A 16 | 0 17 | USER 18 | N 19 | 20 | 21 | 22 | TIMESTAMP 23 | X 24 | TIMESTAMP 25 | 0 26 | X 27 | E 28 | 29 | 30 | COUNTER 31 | X 32 | 0 33 | X 34 | 000004 35 | X 36 | INT4 37 | 000010 38 | INT4 39 | Counter 40 | 41 | 42 | USERNAME 43 | UNAME 44 | 0 45 | E 46 | 47 | 48 | COMMENTS 49 | 0 50 | g 51 | 000008 52 | STRG 53 | STRG 54 | Comment 55 | 56 | 57 | CERT_SUBJECT 58 | 0 59 | g 60 | 000008 61 | STRG 62 | STRG 63 | Certificate Subject 64 | 65 | 66 | CERT_ISSUES 67 | 0 68 | g 69 | 000008 70 | STRG 71 | STRG 72 | Certificate Issuer 73 | 74 | 75 | DATE_FROM 76 | DATEFROM 77 | 0 78 | T 79 | E 80 | 81 | 82 | DATE_TO 83 | DATETO 84 | 0 85 | T 86 | E 87 | 88 | 89 | STATUS 90 | ICON_L2 91 | 0 92 | E 93 | 94 | 95 | MESSAGE 96 | 0 97 | g 98 | 000008 99 | STRG 100 | STRG 101 | Message 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/#apmg#strust_installer.prog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/STRUST_INSTALLER 7 | 1 8 | E 9 | X 10 | X 11 | 12 | 13 | 14 | I 15 | T01 16 | Category 17 | 18 18 | 19 | 20 | I 21 | T02 22 | Certificate 23 | 21 24 | 25 | 26 | I 27 | T03 28 | Options 29 | 17 30 | 31 | 32 | I 33 | T04 34 | API 35 | 80 36 | 37 | 38 | I 39 | T05 40 | Proxy 41 | 80 42 | 43 | 44 | R 45 | Trust Management: Certificate Installer 46 | 39 47 | 48 | 49 | S 50 | P_APPL 51 | Application 52 | 19 53 | 54 | 55 | S 56 | P_CONT 57 | Context 58 | 15 59 | 60 | 61 | S 62 | P_DOMAIN 63 | Domain 64 | 14 65 | 66 | 67 | S 68 | P_ENDPNT 69 | Endpoint 70 | 16 71 | 72 | 73 | S 74 | P_HOST 75 | Host 76 | 12 77 | 78 | 79 | S 80 | P_MAIN 81 | Add domain certificate 82 | 30 83 | 84 | 85 | S 86 | P_PASSWD 87 | Password 88 | 16 89 | 90 | 91 | S 92 | P_PRHOST 93 | Host 94 | 18 95 | 96 | 97 | S 98 | P_PRPASS 99 | Password 100 | 22 101 | 102 | 103 | S 104 | P_PRPORT 105 | Port 106 | 18 107 | 108 | 109 | S 110 | P_PRUSER 111 | User 112 | 18 113 | 114 | 115 | S 116 | P_ROOT 117 | Add root/intermediate certs 118 | 35 119 | 120 | 121 | S 122 | P_SSL_ID 123 | SSL ID 124 | 14 125 | 126 | 127 | S 128 | P_TEST 129 | Test run 130 | 16 131 | 132 | 133 | S 134 | P_TEXT 135 | Description of change 136 | 29 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/#apmg#strust_updater.prog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | /APMG/STRUST_UPDATER 7 | 1 8 | E 9 | X 10 | X 11 | 12 | 13 | 14 | I 15 | T01 16 | Category 17 | 80 18 | 19 | 20 | I 21 | T02 22 | Certificate 23 | 80 24 | 25 | 26 | I 27 | T03 28 | Options 29 | 80 30 | 31 | 32 | I 33 | T04 34 | API 35 | 80 36 | 37 | 38 | I 39 | T05 40 | Proxy 41 | 80 42 | 43 | 44 | R 45 | Trust Management: Certificate Updater 46 | 37 47 | 48 | 49 | S 50 | P_APPL 51 | Application 52 | 19 53 | 54 | 55 | S 56 | P_CONT 57 | Context 58 | 15 59 | 60 | 61 | S 62 | P_DAYS 63 | Minimum days before reneval 64 | 35 65 | 66 | 67 | S 68 | P_ENDPNT 69 | Endpoint 70 | 16 71 | 72 | 73 | S 74 | P_HOST 75 | Host 76 | 12 77 | 78 | 79 | S 80 | P_MAIN 81 | Update domain certificate 82 | 33 83 | 84 | 85 | S 86 | P_PASSWD 87 | Password 88 | 16 89 | 90 | 91 | S 92 | P_PRHOST 93 | Host 94 | 12 95 | 96 | 97 | S 98 | P_PRPASS 99 | Password 100 | 16 101 | 102 | 103 | S 104 | P_PRPORT 105 | Port 106 | 12 107 | 108 | 109 | S 110 | P_PRUSER 111 | User 112 | 12 113 | 114 | 115 | S 116 | P_REMOVE 117 | Remove expired certificates 118 | 35 119 | 120 | 121 | S 122 | P_ROOT 123 | Update root/intermediate certs 124 | 38 125 | 126 | 127 | S 128 | P_SSL_ID 129 | SSL ID 130 | 14 131 | 132 | 133 | S 134 | P_TEST 135 | Test run 136 | 16 137 | 138 | 139 | S 140 | P_TEXT 141 | Description of change 142 | 29 143 | 144 | 145 | S 146 | S_SUBJ 147 | Subject (CN) 148 | 20 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Marc Bernard Tools 2 | 3 | Marc Bernard Tools welcomes your suggestions and contributions! Before opening your first issue or pull request, please review our [Code of Conduct](https://github.com/Marc-Bernard-Tools/.github/blob/main/CODE_OF_CONDUCT.md) to understand how our community interacts in an inclusive and respectful manner. 4 | 5 | ## General Advice 6 | 7 | * If you're new and want to help out, see if there are "good first issues" listed [here](../../issues). They should not be complicated to implement but require you to get the project up and running. Or pick something that annoys you. Fix a typo. Improve an error message. Or try something unusual just to see if it works and if it doesn't, open an issue. 8 | 9 | * Before starting any significant development, open an issue and propose your solution first. A discussion can save a lot of unnecessary work. It also helps others know that this is being worked on. 10 | 11 | * It is in your best interest to keep the commits/PRs as small as possible and solve one thing at a time. The smaller your change is, the easier it is to review and it will be more likely to get accepted. 12 | 13 | * Commit often, whenever something is working, and is a step in the right direction do a commit or PR. This way other contributors can see the changes, and it will minimize the risk of merge conflicts. 14 | 15 | * If you don't have the time or knowledge to fix the problem yourself, you can still make it move along faster by providing an accurate description or a repo which reproduces the issue. 16 | 17 | ## Bug Reports 18 | 19 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - thank you! 20 | 21 | Guidelines for bug reports: 22 | 23 | 1. **Use the GitHub issue search** — check if the issue has already been reported. 24 | 25 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest version or development branch in the repository. 26 | 27 | 3. **Demonstrate the problem** — provide clear steps that can be reproduced. 28 | 29 | A good bug report should not leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What would you expect to be the outcome? All these details will help to fix any potential bugs. 30 | 31 | ## Development Guidelines 32 | 33 | ### Compatibility 34 | 35 | Marc Bernard Tools are targeted for SAP Basis 7.31 and higher, so the code should only contain expressions and statements that works on 7.31. abaplint will automatically check every pull request for language syntax that is not available on these releases. 36 | 37 | ### Linting 38 | 39 | Pull requests are checked using [abaplint](https://abaplint.org) and must pass all configured [checks](../../abaplint.json) before they can be merged. 40 | 41 | ### Pretty Printer 42 | 43 | Use pretty printer, keywords upper case + indentation. 44 | 45 | ### Prefixing 46 | 47 | Variables are prefixed using the standard setting in [abapOpenChecks](http://docs.abapopenchecks.org/checks/69/) naming conventions. 48 | 49 | ### Internationalization (I18N) 50 | 51 | Currently, Marc Bernard Tools support only English language. Neither objects nor text literals are translated. Therefore, all objects shall be set to English as the original language. Text literals in the code shall be maintained in English. 52 | 53 | ## Contribution Licensing 54 | 55 | Most of our code is distributed under the terms of the [license](LICENSE), and when you contribute code that you wrote to our repositories, you agree that you are contributing under those same terms. In addition, by submitting your contributions you are indicating that you have the right to submit those contributions under those terms. 56 | 57 | If you wish to contribute code or documentation *authored by others*, or using the terms of any other license, please indicate that clearly in your pull request so that the project team can discuss the situation with you. 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Version](https://img.shields.io/endpoint?url=https://shield.abappm.com/github/abapPM/ABAP-Strust/src/%2523apmg%2523cl_strust.clas.abap/c_version&label=Version&color=blue) 2 | 3 | [![License](https://img.shields.io/github/license/abapPM/ABAP-Strust?label=License&color=success)](LICENSE) 4 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg?color=success)](https://github.com/abapPM/.github/blob/main/CODE_OF_CONDUCT.md) 5 | [![REUSE Status](https://api.reuse.software/badge/github.com/abapPM/ABAP-Strust)](https://api.reuse.software/info/github.com/abapPM/ABAP-Strust) 6 | 7 | # Trust Management 8 | 9 | Easy-to-use class for adding, updating, or removing certificates from ABAP Trust Management (transaction STRUST) 10 | 11 | NO WARRANTIES, [MIT License](LICENSE) 12 | 13 | ## Usage 14 | 15 | ### Install Certificates 16 | 17 | Run program `/APMG/STRUST_INSTALLER` and enter the domain for which you want to install the required certificates to ABAP Trust Management: 18 | 19 | ![Installer Selection-Screen](https://github.com/abapPM/ABAP-Strust/raw/main/img/installer-1.png) 20 | 21 | ![Installer Result](https://github.com/abapPM/ABAP-Strust/raw/main/img/installer-2.png) 22 | 23 | ### Update Certificates 24 | 25 | Run program `/APMG/STRUST_UPDATER` and optionally enter domains for which you want to update the certificates to ABAP Trust Management: 26 | 27 | ![Updater Selection-Screen](https://github.com/abapPM/ABAP-Strust/raw/main/img/updater-1.png) 28 | 29 | ![Updater Result](https://github.com/abapPM/ABAP-Strust/raw/main/img/updater-2.png) 30 | 31 | ![Updater Result](https://github.com/abapPM/ABAP-Strust/raw/main/img/updater-3.png) 32 | 33 | ![Updater Result with Root Intermediate Certificates](https://github.com/abapPM/ABAP-Strust/raw/main/img/updater-4.png) 34 | 35 | ## API 36 | 37 | Example of creating, updating, or removing a certificate using class `/apmg/cl_strust`. 38 | 39 | ```abap 40 | CONSTANTS: 41 | c_sslc TYPE psecontext VALUE 'SSLC' ##NO_TEXT, 42 | c_anonym TYPE ssfappl VALUE 'ANONYM' ##NO_TEXT, 43 | c_id TYPE ssfid VALUE 'CN=%SID SSL client SSL Client (Standard), OU=%ORG, O=MBT, C=CA' ##NO_TEXT, 44 | c_org TYPE string VALUE 'Marc Bernard Tools' ##NO_TEXT, 45 | c_subject TYPE string VALUE 'CN=*.marcbernardtools.com' ##NO_TEXT. 46 | 47 | DATA(strust) = /apmg/cl_strust=>create( 48 | context = c_sslc 49 | application = c_anonym ). 50 | 51 | strust->load( 52 | create = abap_true 53 | id = c_id 54 | org = c_org ). 55 | 56 | strust->get_own_certificate( ). 57 | 58 | strust->get_certificate_list( ). 59 | 60 | IF drop = abap_true. 61 | strust->remove( c_subject ). 62 | ELSE. 63 | strust->add_pem( '' ). 64 | DATA(result) = strust->update( ). 65 | " result-added contains newly added certificates 66 | " result-removed contains removed certificates (if remove_expired = abap_true) 67 | ENDIF. 68 | ``` 69 | 70 | The certificate for the `add` method needs to be provided as a table with the following format: 71 | 72 | ```txt 73 | -----BEGIN CERTIFICATE----- 74 | MIIGQDCCBSigAwIBAgIQCNqWSvYNNa9hfOzsk89rUjANBgkqhkiG9w0BAQsFADBg 75 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 76 | ... 77 | -----END CERTIFICATE----- 78 | ``` 79 | 80 | Alternatively, use the method `add_pem` to pass the certificate as a string. 81 | 82 | ## Prerequisites 83 | 84 | - SAP Basis 7.50 or higher 85 | - Packages: 86 | - [`ajson`](https://github.com/sbcgua/ajson) 87 | - [`error`](https://github.com/abapPM/ABAP-Error) 88 | - [`distinguished-name`](https://github.com/abapPM/ABAP-Distinguished-Name) 89 | 90 | ## Installation 91 | 92 | Install `strust` as a global module in your system using [apm](https://abappm.com). 93 | 94 | or 95 | 96 | Specify the `strust` module as a dependency in your project and import it to your namespace using [apm](https://abappm.com). 97 | 98 | ## Contributions 99 | 100 | All contributions are welcome! Read our [Contribution Guidelines](https://github.com/abapPM/ABAP-Strust/blob/main/CONTRIBUTING.md), fork this repo, and create a pull request. 101 | 102 | You can install the developer version of ABAP STRUST using [abapGit](https://github.com/abapGit/abapGit) by creating a new online repository for `https://github.com/abapPM/ABAP-Strust`. 103 | 104 | Recommended SAP package: `/APMG/STRUST` 105 | 106 | ## About 107 | 108 | Made with ❤ in Canada 109 | 110 | Copyright 2025 apm.to Inc. 111 | 112 | Follow [@marcf.be](https://bsky.app/profile/marcf.be) on Bluesky and [@marcfbe](https://linkedin.com/in/marcfbe) or LinkedIn 113 | -------------------------------------------------------------------------------- /src/#apmg#cl_strust_cert_api.clas.abap: -------------------------------------------------------------------------------- 1 | CLASS /apmg/cl_strust_cert_api DEFINITION 2 | PUBLIC 3 | FINAL 4 | CREATE PUBLIC. 5 | 6 | ************************************************************************ 7 | * Trust Management: Certificate API 8 | * 9 | * Get peer, intermediate, and root certificates for a domain from 10 | * https://tools.abappm.com 11 | * 12 | * Copyright 2025 apm.to Inc. 13 | * SPDX-License-Identifier: MIT 14 | ************************************************************************ 15 | PUBLIC SECTION. 16 | 17 | CONSTANTS: 18 | c_api_host TYPE string VALUE 'https://tools.abappm.com', 19 | c_api_endpoint TYPE string VALUE '/api/v1/certificates'. 20 | 21 | CLASS-METHODS get_certificates 22 | IMPORTING 23 | !domain TYPE string 24 | !ssl_id TYPE ssfapplssl DEFAULT 'ANONYM' 25 | !debug TYPE abap_bool DEFAULT abap_false 26 | !host TYPE string DEFAULT c_api_host 27 | !endpoint TYPE string DEFAULT c_api_endpoint 28 | !proxy_host TYPE string OPTIONAL 29 | !proxy_service TYPE string OPTIONAL 30 | !proxy_user TYPE string OPTIONAL 31 | !proxy_passwd TYPE string OPTIONAL 32 | RETURNING 33 | VALUE(result) TYPE string 34 | RAISING 35 | /apmg/cx_error. 36 | 37 | PROTECTED SECTION. 38 | PRIVATE SECTION. 39 | 40 | CLASS-METHODS _client 41 | IMPORTING 42 | ssl_id TYPE ssfapplssl 43 | host TYPE string 44 | uri TYPE string 45 | proxy_host TYPE string 46 | proxy_service TYPE string 47 | proxy_user TYPE string 48 | proxy_passwd TYPE string 49 | RETURNING 50 | VALUE(result) TYPE REF TO if_http_client 51 | RAISING 52 | /apmg/cx_error. 53 | 54 | CLASS-METHODS _response 55 | IMPORTING 56 | http_client TYPE REF TO if_http_client 57 | RETURNING 58 | VALUE(result) TYPE REF TO if_http_response 59 | RAISING 60 | /apmg/cx_error. 61 | 62 | CLASS-METHODS _debug 63 | IMPORTING 64 | headers TYPE tihttpnvp 65 | cookies TYPE tihttpcki 66 | debug TYPE abap_bool ##CALLED. 67 | 68 | ENDCLASS. 69 | 70 | 71 | 72 | CLASS /apmg/cl_strust_cert_api IMPLEMENTATION. 73 | 74 | 75 | METHOD get_certificates. 76 | 77 | " We can't query wildcard domains so we try with "api" sub-domain 78 | " TODO: if this fails, loop over a couple other common sub-domains 79 | DATA(query) = replace( val = domain sub = '*' with = 'api' ). 80 | 81 | query = cl_abap_dyn_prg=>escape_xss_url( query ). 82 | 83 | DATA(http_client) = _client( 84 | ssl_id = ssl_id 85 | host = host 86 | proxy_host = proxy_host 87 | proxy_service = proxy_service 88 | proxy_user = proxy_user 89 | proxy_passwd = proxy_passwd 90 | uri = |{ endpoint }?domain={ query }| ). 91 | 92 | DATA(fetch_response) = _response( http_client ). 93 | 94 | " --- Retrieve Certificates from Response --- 95 | 96 | DATA(json_response) = fetch_response->get_cdata( ). 97 | 98 | IF debug = abap_true. 99 | cl_abap_browser=>show_html( html_string = json_response ). 100 | ENDIF. 101 | 102 | IF json_response IS INITIAL OR json_response(1) <> '{'. 103 | RAISE EXCEPTION TYPE /apmg/cx_error_text 104 | EXPORTING 105 | text = |Invalid response (expected JSON): { json_response }|. 106 | ENDIF. 107 | 108 | result = json_response. 109 | 110 | ENDMETHOD. 111 | 112 | 113 | METHOD _client. 114 | 115 | cl_http_client=>create_by_url( 116 | EXPORTING 117 | url = host 118 | ssl_id = ssl_id 119 | proxy_host = proxy_host 120 | proxy_service = proxy_service 121 | proxy_user = proxy_user 122 | proxy_passwd = proxy_passwd 123 | IMPORTING 124 | client = result 125 | EXCEPTIONS 126 | OTHERS = 99 ). 127 | IF sy-subrc <> 0. 128 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 129 | ENDIF. 130 | 131 | result->request->set_header_field( 132 | name = '~request_uri' 133 | value = uri ). 134 | 135 | result->request->set_header_field( 136 | name = 'accept' 137 | value = 'application/json' ). 138 | 139 | ENDMETHOD. 140 | 141 | 142 | METHOD _debug. 143 | 144 | CHECK debug = abap_true. 145 | 146 | DATA(html) = `

Headers:

\n`. 147 | LOOP AT headers ASSIGNING FIELD-SYMBOL(
). 148 | html = html && |

{

-name }: {
-value }

\n|. 149 | ENDLOOP. 150 | 151 | html = html && `

Cookies:

\n`. 152 | LOOP AT cookies ASSIGNING FIELD-SYMBOL(). 153 | html = html && |

{ -name }: { -value }

\n|. 154 | ENDLOOP. 155 | 156 | cl_abap_browser=>show_html( html_string = html ). 157 | 158 | ENDMETHOD. 159 | 160 | 161 | METHOD _response. 162 | 163 | DATA: 164 | status_code TYPE sy-subrc, 165 | message TYPE string. 166 | 167 | http_client->propertytype_accept_cookie = if_http_client=>co_enabled. 168 | 169 | http_client->request->set_version( if_http_entity=>co_protocol_version_1_1 ). 170 | 171 | http_client->send( 172 | EXCEPTIONS 173 | http_communication_failure = 1 174 | http_invalid_state = 2 175 | http_processing_failed = 3 176 | http_invalid_timeout = 4 177 | OTHERS = 5 ). 178 | IF sy-subrc = 0. 179 | http_client->receive( 180 | EXCEPTIONS 181 | http_communication_failure = 1 182 | http_invalid_state = 2 183 | http_processing_failed = 3 184 | OTHERS = 4 ). 185 | ENDIF. 186 | 187 | IF sy-subrc <> 0. 188 | http_client->get_last_error( 189 | IMPORTING 190 | code = status_code 191 | message = message ). 192 | RAISE EXCEPTION TYPE /apmg/cx_error_text EXPORTING text = |{ message } (HTTP/{ status_code })|. 193 | ENDIF. 194 | 195 | result = http_client->response. 196 | 197 | ENDMETHOD. 198 | ENDCLASS. 199 | -------------------------------------------------------------------------------- /LICENSES/CC0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /src/#apmg#strust_installer.prog.abap: -------------------------------------------------------------------------------- 1 | REPORT /apmg/strust_installer LINE-SIZE 255. 2 | 3 | ************************************************************************ 4 | * Trust Management: Certificate Installer 5 | * 6 | * Copyright 2025 apm.to Inc. 7 | * SPDX-License-Identifier: MIT 8 | ************************************************************************ 9 | 10 | SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01. 11 | PARAMETERS: 12 | p_cont TYPE psecontext DEFAULT 'SSLC' OBLIGATORY, 13 | p_appl TYPE ssfappl DEFAULT 'ANONYM' OBLIGATORY. 14 | SELECTION-SCREEN END OF BLOCK b1. 15 | 16 | SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-t02. 17 | PARAMETERS p_domain TYPE string OBLIGATORY LOWER CASE. 18 | SELECTION-SCREEN SKIP. 19 | PARAMETERS p_text TYPE string LOWER CASE. 20 | SELECTION-SCREEN END OF BLOCK b2. 21 | 22 | SELECTION-SCREEN BEGIN OF BLOCK b4 WITH FRAME TITLE TEXT-t04. 23 | PARAMETERS: 24 | p_ssl_id TYPE ssfappl DEFAULT 'ANONYM', 25 | p_host TYPE string OBLIGATORY LOWER CASE, 26 | p_endpnt TYPE string OBLIGATORY LOWER CASE. 27 | SELECTION-SCREEN END OF BLOCK b4. 28 | 29 | SELECTION-SCREEN BEGIN OF BLOCK b5 WITH FRAME TITLE TEXT-t05. 30 | PARAMETERS: 31 | p_prhost TYPE string LOWER CASE, 32 | p_prport TYPE string LOWER CASE, 33 | p_pruser TYPE string LOWER CASE, 34 | p_prpass TYPE string LOWER CASE. 35 | SELECTION-SCREEN END OF BLOCK b5. 36 | 37 | SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-t03. 38 | PARAMETERS: 39 | p_passwd TYPE string LOWER CASE, 40 | p_root AS CHECKBOX DEFAULT abap_true, 41 | p_main AS CHECKBOX DEFAULT abap_true, 42 | p_test AS CHECKBOX DEFAULT abap_true. 43 | SELECTION-SCREEN END OF BLOCK b3. 44 | 45 | INITIALIZATION. 46 | 47 | DATA(subrc) = cl_abap_pse=>authority_check( iv_activity = '01' ) 48 | + cl_abap_pse=>authority_check( iv_activity = '02' ) 49 | + cl_abap_pse=>authority_check( iv_activity = '06' ). 50 | IF subrc <> 0. 51 | MESSAGE 'You are not authorized to install certificates' TYPE 'I' DISPLAY LIKE 'E'. 52 | STOP. 53 | ENDIF. 54 | 55 | p_host = /apmg/cl_strust_cert_api=>c_api_host. 56 | p_endpnt = /apmg/cl_strust_cert_api=>c_api_endpoint. 57 | 58 | START-OF-SELECTION. 59 | 60 | IF p_root IS INITIAL AND p_main IS INITIAL. 61 | MESSAGE 'No certificates selected for installation' TYPE 'I' DISPLAY LIKE 'E'. 62 | STOP. 63 | ENDIF. 64 | 65 | CALL FUNCTION 'SSFPSE_PARAMETER' 66 | EXPORTING 67 | context = p_cont 68 | applic = p_appl 69 | EXCEPTIONS 70 | pse_not_found = 1 71 | OTHERS = 2. 72 | IF sy-subrc <> 0. 73 | MESSAGE 'PSE not found' TYPE 'I' DISPLAY LIKE 'E'. 74 | STOP. 75 | ENDIF. 76 | 77 | TRY. 78 | DATA(strust) = /apmg/cl_strust=>create( 79 | context = p_cont 80 | application = p_appl 81 | password = p_passwd )->load( ). 82 | CATCH /apmg/cx_error INTO DATA(error). 83 | MESSAGE error TYPE 'I' DISPLAY LIKE 'E'. 84 | STOP. 85 | ENDTRY. 86 | 87 | WRITE: /'Domain:', p_domain COLOR COL_POSITIVE. 88 | SKIP. 89 | 90 | TRY. 91 | DATA(json) = /apmg/cl_strust_cert_api=>get_certificates( 92 | ssl_id = p_ssl_id 93 | domain = p_domain 94 | host = p_host 95 | endpoint = p_endpnt 96 | proxy_host = p_prhost 97 | proxy_service = p_prport 98 | proxy_user = p_pruser 99 | proxy_passwd = p_prpass ). 100 | 101 | TRY. 102 | DATA(ajson) = zcl_ajson=>parse( json ). 103 | CATCH zcx_ajson_error INTO DATA(ajson_error). 104 | WRITE: / 'Error parsing API response:' COLOR COL_NEGATIVE, ajson_error->get_text( ). 105 | STOP. 106 | ENDTRY. 107 | 108 | IF ajson->get( '/error' ) IS NOT INITIAL. 109 | WRITE: / 'Error getting certificates from API:' COLOR COL_NEGATIVE, ajson->get( '/error' ). 110 | STOP. 111 | ENDIF. 112 | 113 | " Keep fingers crossed that the response matches what we need for the update 114 | DATA(cert_domain) = ajson->get( '/domain' ). 115 | 116 | IF cert_domain <> p_domain AND p_domain NA '*'. 117 | WRITE: / 'Certificates domain does not match request:' COLOR COL_TOTAL, cert_domain. 118 | STOP. 119 | ENDIF. 120 | 121 | " We finally have a certificate that can be used for the install, yay! 122 | 123 | " Root and intermediate certificates 124 | IF p_root = abap_true. 125 | 126 | LOOP AT ajson->members( '/intermediateCertificates' ) INTO DATA(member). 127 | 128 | DATA(inter_pem) = ajson->get( '/intermediateCertificates/' && member && '/pem' ). 129 | DATA(inter_date_from) = ajson->get( '/intermediateCertificates/' && member && '/validFrom' ). 130 | DATA(inter_date_to) = ajson->get( '/intermediateCertificates/' && member && '/validTo' ). 131 | DATA(inter_subject) = 'CN=' && ajson->get( '/intermediateCertificates/' && member && '/subject/CN' ). 132 | IF inter_subject = 'CN='. 133 | inter_subject = 'O=' && ajson->get( '/intermediateCertificates/' && member && '/subject/O' ). 134 | ENDIF. 135 | IF strlen( inter_subject ) > 78. 136 | inter_subject = inter_subject(75) && '...'. 137 | ENDIF. 138 | 139 | IF p_test = abap_false. 140 | strust->add_pem( inter_pem ). 141 | ENDIF. 142 | 143 | WRITE: /10 'Root/intermediate certificate added:' COLOR COL_POSITIVE, 144 | AT 50 inter_subject, 145 | AT 130 inter_date_from(10), 146 | AT 145 inter_date_to(10), 147 | AT 158 ''. 148 | 149 | ENDLOOP. 150 | SKIP. 151 | 152 | ENDIF. 153 | 154 | " Domain certificate 155 | IF p_main = abap_true. 156 | 157 | DATA(peer_pem) = ajson->get( '/peerCertificate/pem' ). 158 | DATA(peer_date_from) = ajson->get( '/peerCertificate/validFrom' ). 159 | DATA(peer_date_to) = ajson->get( '/peerCertificate/validTo' ). 160 | DATA(peer_subject) = 'CN=' && ajson->get( '/peerCertificate/subject/CN' ). 161 | IF peer_subject = 'CN='. 162 | peer_subject = 'O=' && ajson->get( '/peerCertificate/subject/O' ). 163 | ENDIF. 164 | IF strlen( peer_subject ) > 78. 165 | peer_subject = peer_subject(75) && '...'. 166 | ENDIF. 167 | 168 | IF p_test = abap_false. 169 | strust->add_pem( peer_pem ). 170 | ENDIF. 171 | 172 | WRITE: /10 'New certificate added:' COLOR COL_POSITIVE, 173 | AT 50 peer_subject, 174 | AT 130 peer_date_from(10), 175 | AT 145 peer_date_to(10), 176 | AT 158 ''. 177 | 178 | ENDIF. 179 | 180 | ULINE. 181 | 182 | IF p_test = abap_true. 183 | WRITE: / 'Test run' COLOR COL_TOTAL, '(changes were not saved)'. 184 | ELSE. 185 | 186 | DATA(install_result) = strust->update( p_text ). 187 | 188 | " Display summary of changes 189 | IF lines( install_result-added ) > 0. 190 | WRITE / |{ lines( install_result-added ) } certificate(s) added| COLOR COL_POSITIVE. 191 | ENDIF. 192 | IF lines( install_result-removed ) > 0. 193 | WRITE / |{ lines( install_result-removed ) } certificate(s) removed| COLOR COL_TOTAL. 194 | ENDIF. 195 | 196 | WRITE / 'Certificates saved' COLOR COL_POSITIVE. 197 | 198 | ENDIF. 199 | 200 | CATCH /apmg/cx_error INTO error. 201 | WRITE: / 'Error updating certificate:' COLOR COL_NEGATIVE, error->get_text( ). 202 | ENDTRY. 203 | -------------------------------------------------------------------------------- /abaplint.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "files": "/src/**/*.*", 5 | "folder": "/deps", 6 | "url": "https://github.com/abaplint/deps" 7 | }, 8 | { 9 | "files": "/src/**/*.*", 10 | "folder": "/deps2", 11 | "url": "https://github.com/abapPM/ABAP-Error" 12 | }, 13 | { 14 | "files": "/src/**/*.*", 15 | "folder": "/deps4", 16 | "url": "https://github.com/abapPM/ABAP-Distinguished-Name" 17 | }, 18 | { 19 | "files": "/src/**/*.*", 20 | "folder": "/deps5", 21 | "url": "https://github.com/sbcgua/ajson" 22 | } 23 | ], 24 | "global": { 25 | "exclude": [], 26 | "files": "/src/**/*.*", 27 | "noIssues": [], 28 | "skipGeneratedBOPFInterfaces": true, 29 | "skipGeneratedFunctionGroups": true, 30 | "skipGeneratedGatewayClasses": true, 31 | "skipGeneratedPersistentClasses": true, 32 | "skipGeneratedProxyClasses": true, 33 | "skipGeneratedProxyInterfaces": true, 34 | "skipIncludesWithoutMain": true, 35 | "useApackDependencies": false 36 | }, 37 | "rules": { 38 | "7bit_ascii": false, 39 | "abapdoc": false, 40 | "add_test_attributes": true, 41 | "align_parameters": false, 42 | "align_pseudo_comments": true, 43 | "align_type_expressions": true, 44 | "allowed_object_naming": true, 45 | "allowed_object_types": { 46 | "allowed": [ 47 | "CLAS", 48 | "DEVC", 49 | "INTF", 50 | "NSPC", 51 | "PROG", 52 | "TABL" 53 | ] 54 | }, 55 | "ambiguous_statement": true, 56 | "avoid_use": true, 57 | "begin_end_names": true, 58 | "begin_single_include": true, 59 | "call_transaction_authority_check": true, 60 | "cds_comment_style": true, 61 | "cds_legacy_view": true, 62 | "cds_parser_error": true, 63 | "chain_mainly_declarations": true, 64 | "change_if_to_case": true, 65 | "check_abstract": true, 66 | "check_comments": { 67 | "allowEndOfLine": true 68 | }, 69 | "check_ddic": true, 70 | "check_include": true, 71 | "check_subrc": true, 72 | "check_syntax": true, 73 | "check_text_elements": true, 74 | "check_transformation_exists": true, 75 | "class_attribute_names": false, 76 | "classic_exceptions_overlap": true, 77 | "cloud_types": true, 78 | "colon_missing_space": true, 79 | "commented_code": true, 80 | "constant_classes": true, 81 | "constructor_visibility_public": true, 82 | "contains_tab": true, 83 | "cyclic_oo": true, 84 | "cyclomatic_complexity": true, 85 | "dangerous_statement": true, 86 | "db_operation_in_loop": false, 87 | "definitions_top": true, 88 | "description_empty": true, 89 | "double_space": true, 90 | "downport": true, 91 | "dynpro_checks": true, 92 | "easy_to_find_messages": true, 93 | "empty_event": true, 94 | "empty_line_in_statement": true, 95 | "empty_statement": true, 96 | "empty_structure": false, 97 | "exit_or_check": { 98 | "allowCheck": true, 99 | "allowExit": false 100 | }, 101 | "expand_macros": true, 102 | "exporting": true, 103 | "forbidden_identifier": true, 104 | "forbidden_pseudo_and_pragma": true, 105 | "forbidden_void_type": { 106 | "check": [ 107 | "^STRINGTAB$", 108 | "^CHAR[0-9]+$", 109 | "^INT4$", 110 | "^RS_C_TRUE$", 111 | "^RS_C_FALSE$", 112 | "^FLAG$", 113 | "^SYDATUM$", 114 | "^SYINDEX$", 115 | "^SYTABIX$", 116 | "^SYUZEIT$" 117 | ] 118 | }, 119 | "form_tables_obsolete": true, 120 | "fully_type_constants": true, 121 | "fully_type_itabs": true, 122 | "function_module_recommendations": true, 123 | "functional_writing": true, 124 | "global_class": true, 125 | "identical_conditions": true, 126 | "identical_contents": true, 127 | "identical_descriptions": true, 128 | "identical_form_names": true, 129 | "if_in_if": false, 130 | "implement_methods": true, 131 | "implicit_start_of_selection": true, 132 | "in_statement_indentation": false, 133 | "indentation": { 134 | "ignoreExceptions": true, 135 | "alignTryCatch": false, 136 | "selectionScreenBlockIndentation": true, 137 | "globalClassSkipFirst": false, 138 | "ignoreGlobalClassDefinition": false, 139 | "ignoreGlobalInterface": false 140 | }, 141 | "inline_data_old_versions": true, 142 | "intf_referencing_clas": true, 143 | "invalid_table_index": true, 144 | "keep_single_parameter_on_one_line": true, 145 | "keyword_case": true, 146 | "line_break_multiple_parameters": { 147 | "count": 3 148 | }, 149 | "line_break_style": true, 150 | "line_length": true, 151 | "line_only_punc": true, 152 | "local_class_naming": true, 153 | "local_testclass_consistency": true, 154 | "local_variable_names": false, 155 | "macro_naming": true, 156 | "main_file_contents": true, 157 | "many_parentheses": true, 158 | "max_one_method_parameter_per_line": true, 159 | "max_one_statement": true, 160 | "message_exists": true, 161 | "method_implemented_twice": true, 162 | "method_length": true, 163 | "method_overwrites_builtin": true, 164 | "method_parameter_names": false, 165 | "mix_returning": true, 166 | "modify_only_own_db_tables": { 167 | "ownTables": "/APMG/STRUST_LOG" 168 | }, 169 | "msag_consistency": true, 170 | "names_no_dash": true, 171 | "nesting": true, 172 | "newline_between_methods": true, 173 | "no_aliases": true, 174 | "no_chained_assignment": true, 175 | "no_external_form_calls": true, 176 | "no_inline_in_optional_branches": false, 177 | "no_prefixes": { 178 | "data": "^[LGM].?_", 179 | "statics": "^S.?_", 180 | "fieldSymbols": "^<[LGM].?_", 181 | "constants": "^[LGM]C_", 182 | "types": "^.*_TY_", 183 | "methodParameters": "^[ICER].?_", 184 | "allowIsPrefixBoolean": true 185 | }, 186 | "no_public_attributes": { 187 | "allowReadOnly": true, 188 | "ignoreTestClasses": false 189 | }, 190 | "no_yoda_conditions": true, 191 | "nrob_consistency": true, 192 | "object_naming": { 193 | "aqbg": "^Z", 194 | "aqqu": "^Z", 195 | "aqsg": "^ZM", 196 | "auth": "^Z", 197 | "clas": "^/APMG/C(L|X)_STRUST", 198 | "cmod": "^Z", 199 | "doma": "^Z", 200 | "dtel": "^Z", 201 | "enho": "^Z", 202 | "enhs": "^Z", 203 | "enqu": "^EZ", 204 | "form": "^Z", 205 | "fugr": "^Z", 206 | "idoc": "^Z", 207 | "ignoreNames": [], 208 | "ignorePatterns": [], 209 | "intf": "^/APMG/IF_STRUST", 210 | "msag": "^Z", 211 | "patternKind": "required", 212 | "pinf": "^Z", 213 | "prog": "^/APMG/STRUST", 214 | "sfpf": "^Z", 215 | "sfpi": "^Z", 216 | "shlp": "^Z", 217 | "ssfo": "^Z", 218 | "ssst": "^Z", 219 | "sucu": "^Z", 220 | "suso": "^Z", 221 | "sxci": "^Z", 222 | "tabl": "^/APMG/STRUST", 223 | "tran": "^Z", 224 | "ttyp": "^Z", 225 | "wdya": "^Z", 226 | "wdyn": "^Z", 227 | "xslt": "^Z" 228 | }, 229 | "obsolete_statement": true, 230 | "omit_parameter_name": true, 231 | "omit_preceding_zeros": true, 232 | "omit_receiving": true, 233 | "parser_702_chaining": true, 234 | "parser_error": true, 235 | "parser_missing_space": true, 236 | "pragma_style": true, 237 | "prefer_corresponding": true, 238 | "prefer_inline": true, 239 | "prefer_is_not": true, 240 | "prefer_pragmas": true, 241 | "prefer_raise_exception_new": true, 242 | "prefer_returning_to_exporting": true, 243 | "prefer_xsdbool": true, 244 | "preferred_compare_operator": true, 245 | "prefix_is_current_class": true, 246 | "reduce_procedural_code": true, 247 | "reduce_string_templates": true, 248 | "release_idoc": true, 249 | "remove_descriptions": true, 250 | "rfc_error_handling": true, 251 | "select_add_order_by": false, 252 | "select_performance": true, 253 | "select_single_full_key": true, 254 | "selection_screen_naming": true, 255 | "sequential_blank": true, 256 | "short_case": true, 257 | "sicf_consistency": true, 258 | "slow_parameter_passing": true, 259 | "smim_consistency": true, 260 | "space_before_colon": true, 261 | "space_before_dot": true, 262 | "sql_escape_host_variables": true, 263 | "sql_value_conversion": true, 264 | "start_at_tab": true, 265 | "static_call_via_instance": true, 266 | "strict_sql": false, 267 | "superclass_final": true, 268 | "superfluous_value": true, 269 | "sy_modification": true, 270 | "tabl_enhancement_category": true, 271 | "tables_declared_locally": true, 272 | "try_without_catch": true, 273 | "type_form_parameters": true, 274 | "types_naming": true, 275 | "uncaught_exception": true, 276 | "unknown_types": true, 277 | "unnecessary_chaining": true, 278 | "unnecessary_pragma": true, 279 | "unnecessary_return": true, 280 | "unreachable_code": true, 281 | "unsecure_fae": true, 282 | "unused_ddic": true, 283 | "unused_macros": true, 284 | "unused_methods": true, 285 | "unused_types": true, 286 | "unused_variables": true, 287 | "use_bool_expression": true, 288 | "use_class_based_exceptions": true, 289 | "use_line_exists": true, 290 | "use_new": true, 291 | "when_others_last": true, 292 | "whitespace_end": true, 293 | "xml_consistency": true 294 | }, 295 | "syntax": { 296 | "errorNamespace": "^(/APMG/|LCL_|TY_|LIF_)", 297 | "globalConstants": [], 298 | "globalMacros": [], 299 | "version": "v750" 300 | } 301 | } 302 | 303 | -------------------------------------------------------------------------------- /src/#apmg#strust_updater.prog.abap: -------------------------------------------------------------------------------- 1 | REPORT /apmg/strust_updater LINE-SIZE 255. 2 | 3 | ************************************************************************ 4 | * Trust Management: Certificate Updater 5 | * 6 | * Copyright 2025 apm.to Inc. 7 | * SPDX-License-Identifier: MIT 8 | ************************************************************************ 9 | 10 | SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01. 11 | PARAMETERS: 12 | p_cont TYPE psecontext DEFAULT 'SSLC' OBLIGATORY, 13 | p_appl TYPE ssfappl DEFAULT 'ANONYM' OBLIGATORY. 14 | SELECTION-SCREEN END OF BLOCK b1. 15 | 16 | SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-t02. 17 | SELECT-OPTIONS s_subj FOR ('STRING') NO INTERVALS. 18 | SELECTION-SCREEN SKIP. 19 | PARAMETERS p_text TYPE string LOWER CASE. 20 | SELECTION-SCREEN END OF BLOCK b2. 21 | 22 | SELECTION-SCREEN BEGIN OF BLOCK b4 WITH FRAME TITLE TEXT-t04. 23 | PARAMETERS: 24 | p_ssl_id TYPE ssfappl DEFAULT 'ANONYM', 25 | p_host TYPE string OBLIGATORY LOWER CASE, 26 | p_endpnt TYPE string OBLIGATORY LOWER CASE. 27 | SELECTION-SCREEN END OF BLOCK b4. 28 | 29 | SELECTION-SCREEN BEGIN OF BLOCK b5 WITH FRAME TITLE TEXT-t05. 30 | PARAMETERS: 31 | p_prhost TYPE string LOWER CASE, 32 | p_prport TYPE string LOWER CASE, 33 | p_pruser TYPE string LOWER CASE, 34 | p_prpass TYPE string LOWER CASE. 35 | SELECTION-SCREEN END OF BLOCK b5. 36 | 37 | SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-t03. 38 | PARAMETERS: 39 | p_days TYPE i DEFAULT 30, 40 | p_passwd TYPE string LOWER CASE, 41 | p_root AS CHECKBOX DEFAULT abap_true, 42 | p_main AS CHECKBOX DEFAULT abap_true, 43 | p_remove AS CHECKBOX DEFAULT abap_true, 44 | p_test AS CHECKBOX DEFAULT abap_true. 45 | SELECTION-SCREEN END OF BLOCK b3. 46 | 47 | INITIALIZATION. 48 | 49 | DATA(subrc) = cl_abap_pse=>authority_check( iv_activity = '01' ) 50 | + cl_abap_pse=>authority_check( iv_activity = '02' ) 51 | + cl_abap_pse=>authority_check( iv_activity = '06' ). 52 | IF subrc <> 0. 53 | MESSAGE 'You are not authorized to update certificates' TYPE 'I' DISPLAY LIKE 'E'. 54 | STOP. 55 | ENDIF. 56 | 57 | p_host = /apmg/cl_strust_cert_api=>c_api_host. 58 | p_endpnt = /apmg/cl_strust_cert_api=>c_api_endpoint. 59 | 60 | START-OF-SELECTION. 61 | 62 | IF p_root IS INITIAL AND p_main IS INITIAL. 63 | MESSAGE 'No certificates selected for update' TYPE 'I' DISPLAY LIKE 'E'. 64 | STOP. 65 | ENDIF. 66 | 67 | CALL FUNCTION 'SSFPSE_PARAMETER' 68 | EXPORTING 69 | context = p_cont 70 | applic = p_appl 71 | EXCEPTIONS 72 | pse_not_found = 1 73 | OTHERS = 2. 74 | IF sy-subrc <> 0. 75 | MESSAGE 'PSE not found' TYPE 'I' DISPLAY LIKE 'E'. 76 | STOP. 77 | ENDIF. 78 | 79 | TRY. 80 | DATA(strust) = /apmg/cl_strust=>create( 81 | context = p_cont 82 | application = p_appl 83 | password = p_passwd )->load( ). 84 | 85 | DATA(certs) = strust->get_certificate_list( ). 86 | CATCH /apmg/cx_error INTO DATA(error). 87 | MESSAGE error TYPE 'I' DISPLAY LIKE 'E'. 88 | STOP. 89 | ENDTRY. 90 | 91 | SORT certs BY date_to date_from. 92 | 93 | LOOP AT certs ASSIGNING FIELD-SYMBOL() WHERE subject IN s_subj. 94 | 95 | DATA(days_until_expire) = -date_to - sy-datum. 96 | 97 | WRITE: / -subject, 98 | AT 130 |{ -date_from DATE = ISO }|, 99 | AT 145 |{ -date_to DATE = ISO }|, 100 | AT 158 ''. 101 | 102 | IF days_until_expire > 30. 103 | WRITE 'valid' COLOR COL_POSITIVE. 104 | ELSEIF days_until_expire > 7. 105 | WRITE 'expires in a month' COLOR COL_TOTAL. 106 | ELSEIF days_until_expire > 0. 107 | WRITE 'expires in a week' COLOR COL_GROUP. 108 | ELSE. 109 | WRITE 'expired' COLOR COL_NEGATIVE. 110 | ENDIF. 111 | 112 | IF days_until_expire > p_days. 113 | CONTINUE. 114 | ENDIF. 115 | 116 | " Get the domain of the certificate 117 | DATA(dn) = /apmg/cl_distinguished_name=>parse( -subject ). 118 | 119 | IF NOT line_exists( dn[ key = 'CN' ] ). 120 | WRITE /5 'Unable to determine CN of certificate subject' COLOR COL_NEGATIVE. 121 | ULINE. 122 | CONTINUE. 123 | ENDIF. 124 | 125 | DATA(domain) = dn[ key = 'CN' ]-name. 126 | 127 | IF domain NA '.'. 128 | " It's probably a root or intermediate certificate 129 | " -> Go to their website to download and install the certificate manually 130 | WRITE /5 'Unable to determine domain of certificate' COLOR COL_TOTAL. 131 | ULINE. 132 | CONTINUE. 133 | ENDIF. 134 | 135 | WRITE: /5 'Domain:', domain COLOR COL_POSITIVE. 136 | SKIP. 137 | 138 | TRY. 139 | DATA(json) = /apmg/cl_strust_cert_api=>get_certificates( 140 | ssl_id = p_ssl_id 141 | domain = domain 142 | host = p_host 143 | endpoint = p_endpnt 144 | proxy_host = p_prhost 145 | proxy_service = p_prport 146 | proxy_user = p_pruser 147 | proxy_passwd = p_prpass ). 148 | 149 | TRY. 150 | DATA(ajson) = zcl_ajson=>parse( json ). 151 | CATCH zcx_ajson_error INTO DATA(ajson_error). 152 | WRITE: /10 'Error parsing API response:' COLOR COL_NEGATIVE, ajson_error->get_text( ). 153 | ULINE. 154 | CONTINUE. 155 | ENDTRY. 156 | 157 | IF ajson->get( '/error' ) IS NOT INITIAL. 158 | WRITE: /10 'Error getting certificates from API:' COLOR COL_NEGATIVE, ajson->get( '/error' ). 159 | ULINE. 160 | CONTINUE. 161 | ENDIF. 162 | 163 | " Keep fingers crossed that the response matches what we need for the update 164 | DATA(cert_domain) = ajson->get( '/domain' ). 165 | 166 | IF cert_domain <> domain AND domain NA '*'. 167 | WRITE: /10 'Certificates domain does not match request:' COLOR COL_TOTAL, cert_domain. 168 | ULINE. 169 | CONTINUE. 170 | ENDIF. 171 | 172 | " We finally have a certificate that can be used for the update, yay! 173 | " Root and intermediate certificates 174 | IF p_root = abap_true. 175 | 176 | LOOP AT ajson->members( '/intermediateCertificates' ) INTO DATA(member). 177 | 178 | DATA(inter_pem) = ajson->get( '/intermediateCertificates/' && member && '/pem' ). 179 | DATA(inter_date_from) = ajson->get( '/intermediateCertificates/' && member && '/validFrom' ). 180 | DATA(inter_date_to) = ajson->get( '/intermediateCertificates/' && member && '/validTo' ). 181 | DATA(inter_subject) = 'CN=' && ajson->get( '/intermediateCertificates/' && member && '/subject/CN' ). 182 | IF inter_subject = 'CN='. 183 | inter_subject = 'O=' && ajson->get( '/intermediateCertificates/' && member && '/subject/O' ). 184 | ENDIF. 185 | IF strlen( inter_subject ) > 78. 186 | inter_subject = inter_subject(75) && '...'. 187 | ENDIF. 188 | 189 | IF p_test = abap_false. 190 | strust->add_pem( inter_pem ). 191 | ENDIF. 192 | 193 | WRITE: /10 'Root/intermediate certificate added:' COLOR COL_POSITIVE, 194 | AT 50 inter_subject, 195 | AT 130 inter_date_from(10), 196 | AT 145 inter_date_to(10), 197 | AT 158 ''. 198 | 199 | ENDLOOP. 200 | SKIP. 201 | 202 | ENDIF. 203 | 204 | " Domain certificate 205 | IF p_main = abap_true. 206 | 207 | DATA(peer_pem) = ajson->get( '/peerCertificate/pem' ). 208 | DATA(peer_date_from) = ajson->get( '/peerCertificate/validFrom' ). 209 | DATA(peer_date_to) = ajson->get( '/peerCertificate/validTo' ). 210 | DATA(peer_subject) = 'CN=' && ajson->get( '/peerCertificate/subject/CN' ). 211 | IF peer_subject = 'CN='. 212 | peer_subject = 'O=' && ajson->get( '/peerCertificate/subject/O' ). 213 | ENDIF. 214 | IF strlen( peer_subject ) > 78. 215 | peer_subject = peer_subject(75) && '...'. 216 | ENDIF. 217 | 218 | IF p_test = abap_false. 219 | strust->add_pem( peer_pem ). 220 | ENDIF. 221 | 222 | WRITE: /10 'New certificate added' COLOR COL_POSITIVE, 223 | AT 50 peer_subject, 224 | AT 130 peer_date_from(10), 225 | AT 145 peer_date_to(10), 226 | AT 158 ''. 227 | 228 | ENDIF. 229 | 230 | CATCH /apmg/cx_error INTO error. 231 | WRITE: /10 'Error updating certificate:' COLOR COL_NEGATIVE, error->get_text( ). 232 | ENDTRY. 233 | 234 | ULINE. 235 | ENDLOOP. 236 | 237 | IF sy-subrc <> 0. 238 | WRITE / 'No certificates found' COLOR COL_TOTAL. 239 | STOP. 240 | ENDIF. 241 | 242 | ULINE. 243 | 244 | IF p_test = abap_true. 245 | WRITE: / 'Test run' COLOR COL_TOTAL, '(changes were not saved)'. 246 | STOP. 247 | ENDIF. 248 | 249 | " Save changes 250 | TRY. 251 | DATA(update_result) = strust->update( 252 | comment = p_text 253 | remove_expired = p_remove ). 254 | 255 | " Display removed certificates 256 | IF lines( update_result-removed ) > 0. 257 | WRITE / 'Removed certificates:' COLOR COL_TOTAL. 258 | LOOP AT update_result-removed ASSIGNING FIELD-SYMBOL(). 259 | WRITE: /5 -subject COLOR COL_NEGATIVE, 260 | AT 130 |{ -date_from DATE = ISO }|, 261 | AT 145 |{ -date_to DATE = ISO }|, 262 | AT 158 'removed'. 263 | ENDLOOP. 264 | SKIP. 265 | ENDIF. 266 | 267 | " Display added certificates 268 | IF lines( update_result-added ) > 0. 269 | WRITE / 'Added certificates:' COLOR COL_POSITIVE. 270 | LOOP AT update_result-added ASSIGNING FIELD-SYMBOL(). 271 | WRITE: /5 -subject COLOR COL_POSITIVE, 272 | AT 130 |{ -date_from DATE = ISO }|, 273 | AT 145 |{ -date_to DATE = ISO }|, 274 | AT 158 'added'. 275 | ENDLOOP. 276 | SKIP. 277 | ENDIF. 278 | 279 | WRITE / 'Certificates saved' COLOR COL_POSITIVE. 280 | CATCH /apmg/cx_error INTO error. 281 | WRITE: / 'Error updating certificate:' COLOR COL_NEGATIVE, error->get_text( ). 282 | ENDTRY. 283 | -------------------------------------------------------------------------------- /src/#apmg#cl_strust.clas.abap: -------------------------------------------------------------------------------- 1 | CLASS /apmg/cl_strust DEFINITION 2 | PUBLIC 3 | FINAL 4 | CREATE PUBLIC. 5 | 6 | ************************************************************************ 7 | * Trust Management 8 | * 9 | * Add, update, or remove certificates from ABAP Trust Management 10 | * 11 | * Copyright 2024 apm.to Inc. 12 | * SPDX-License-Identifier: MIT 13 | ************************************************************************ 14 | PUBLIC SECTION. 15 | 16 | CONSTANTS c_version TYPE string VALUE '2.2.0' ##NEEDED. 17 | 18 | CONSTANTS: 19 | BEGIN OF c_context ##NEEDED, 20 | prog TYPE psecontext VALUE 'PROG', " Namespace of transaction STRUST 21 | smim TYPE psecontext VALUE 'SMIM', " Namespace of table STRUSTSMIM 22 | ssfa TYPE psecontext VALUE 'SSFA', " Namespace of table SSFARGS 23 | ssfv TYPE psecontext VALUE 'SSFV', " Namespace of table SSFVKEYDEF 24 | sslc TYPE psecontext VALUE 'SSLC', " Namespace of table STRUSTSSL 25 | ssls TYPE psecontext VALUE 'SSLS', " Namespace of table STRUSTSSLS 26 | wsse TYPE psecontext VALUE 'WSSE', " Namespace of table STRUSTWSSE 27 | END OF c_context, 28 | BEGIN OF c_application ##NEEDED, 29 | syst TYPE ssfappl VALUE '', " PROG: System PSE 30 | sncs TYPE ssfappl VALUE '', " PROG: SNC SAP Cryptolib 31 | file TYPE ssfappl VALUE '', " PROG: Files 32 | ssls TYPE ssfappl VALUE '', " PROG: SSL backward compatibility 33 | spki TYPE ssfappl VALUE '', " SSLC: System PKI 34 | dfault TYPE ssfappl VALUE 'DFAULT', " SSLC,SSLS,WSSE: SSL Client/Server: Standard 35 | anonym TYPE ssfappl VALUE 'ANONYM', " SSLC: SSL Client: Anonymous 36 | sapsup TYPE ssfappl VALUE 'SAPSUP', " SSLC: SSL Client: SAP Support Portal 37 | wsse TYPE ssfappl VALUE 'WSSE', " WSSE: SSL Client: Web Service Security 38 | wsscrt TYPE ssfappl VALUE 'WSSCRT', " WSSE: Other System Encryption Certificates 39 | wwkey TYPE ssfappl VALUE 'WSSKEY', " WSSE: WS Security Keys 40 | END OF c_application. 41 | 42 | TYPES: 43 | ty_line TYPE c LENGTH 80, 44 | ty_certificate TYPE STANDARD TABLE OF ty_line WITH KEY table_line, 45 | BEGIN OF ty_certattr, 46 | subject TYPE string, 47 | issuer TYPE string, 48 | serialno TYPE string, 49 | validfrom TYPE string, 50 | validto TYPE string, 51 | date_from TYPE d, 52 | date_to TYPE d, 53 | certificate TYPE xstring, 54 | END OF ty_certattr, 55 | ty_certattr_tt TYPE STANDARD TABLE OF ty_certattr WITH KEY subject issuer serialno validfrom validto, 56 | BEGIN OF ty_update_result, 57 | added TYPE ty_certattr_tt, 58 | removed TYPE ty_certattr_tt, 59 | END OF ty_update_result. 60 | 61 | CLASS-METHODS create 62 | IMPORTING 63 | !context TYPE psecontext 64 | !application TYPE ssfappl 65 | !password TYPE string OPTIONAL 66 | RETURNING 67 | VALUE(result) TYPE REF TO /apmg/cl_strust 68 | RAISING 69 | /apmg/cx_error. 70 | 71 | METHODS constructor 72 | IMPORTING 73 | !context TYPE psecontext 74 | !application TYPE ssfappl 75 | !password TYPE string OPTIONAL 76 | RAISING 77 | /apmg/cx_error. 78 | 79 | METHODS load 80 | IMPORTING 81 | !create TYPE abap_bool DEFAULT abap_false 82 | !id TYPE ssfid OPTIONAL 83 | !org TYPE string OPTIONAL 84 | RETURNING 85 | VALUE(result) TYPE REF TO /apmg/cl_strust 86 | RAISING 87 | /apmg/cx_error. 88 | 89 | METHODS add 90 | IMPORTING 91 | !certificate TYPE ty_certificate 92 | RETURNING 93 | VALUE(result) TYPE REF TO /apmg/cl_strust 94 | RAISING 95 | /apmg/cx_error. 96 | 97 | METHODS add_pem 98 | IMPORTING 99 | !pem TYPE string 100 | RETURNING 101 | VALUE(result) TYPE REF TO /apmg/cl_strust 102 | RAISING 103 | /apmg/cx_error. 104 | 105 | METHODS get_own_certificate 106 | RETURNING 107 | VALUE(result) TYPE ty_certattr 108 | RAISING 109 | /apmg/cx_error. 110 | 111 | METHODS get_certificate_list 112 | RETURNING 113 | VALUE(result) TYPE ty_certattr_tt 114 | RAISING 115 | /apmg/cx_error. 116 | 117 | METHODS remove 118 | IMPORTING 119 | !subject TYPE string 120 | !comment TYPE string OPTIONAL 121 | RETURNING 122 | VALUE(result) TYPE REF TO /apmg/cl_strust 123 | RAISING 124 | /apmg/cx_error. 125 | 126 | METHODS update 127 | IMPORTING 128 | !comment TYPE string OPTIONAL 129 | !remove_expired TYPE abap_bool DEFAULT abap_false 130 | PREFERRED PARAMETER comment 131 | RETURNING 132 | VALUE(result) TYPE ty_update_result 133 | RAISING 134 | /apmg/cx_error. 135 | 136 | PROTECTED SECTION. 137 | PRIVATE SECTION. 138 | 139 | DATA: 140 | context TYPE psecontext, 141 | applic TYPE ssfappl, 142 | psename TYPE ssfpsename, 143 | psetext TYPE strustappltxt ##NEEDED, 144 | distrib TYPE ssfflag, 145 | tempfile TYPE localfile, 146 | id TYPE ssfid, 147 | profile TYPE ssfpab, 148 | profilepw TYPE ssfpabpw, 149 | cert_own TYPE xstring, 150 | certs_new TYPE ty_certattr_tt, 151 | certs_removed TYPE ty_certattr_tt, 152 | cert_current TYPE ty_certattr, 153 | certs_current TYPE ty_certattr_tt, 154 | logs TYPE STANDARD TABLE OF /apmg/strust_log WITH KEY timestamp counter, 155 | is_dirty TYPE abap_bool. 156 | 157 | METHODS _create 158 | IMPORTING 159 | !id TYPE ssfid OPTIONAL 160 | !org TYPE string OPTIONAL 161 | RAISING 162 | /apmg/cx_error. 163 | 164 | METHODS _lock 165 | RAISING 166 | /apmg/cx_error. 167 | 168 | METHODS _profile 169 | RAISING 170 | /apmg/cx_error. 171 | 172 | METHODS _unlock 173 | RAISING 174 | /apmg/cx_error. 175 | 176 | METHODS _save 177 | RAISING 178 | /apmg/cx_error. 179 | 180 | METHODS _log_add 181 | IMPORTING 182 | !subject TYPE ty_certattr-subject 183 | !issuer TYPE ty_certattr-issuer 184 | !date_from TYPE ty_certattr-date_from 185 | !date_to TYPE ty_certattr-date_to 186 | !status TYPE /apmg/strust_log-status 187 | !message TYPE /apmg/strust_log-message 188 | RAISING 189 | /apmg/cx_error. 190 | 191 | METHODS _log_save 192 | IMPORTING 193 | !comment TYPE string 194 | RAISING 195 | /apmg/cx_error. 196 | 197 | ENDCLASS. 198 | 199 | 200 | 201 | CLASS /apmg/cl_strust IMPLEMENTATION. 202 | 203 | 204 | METHOD add. 205 | 206 | DATA cert_new TYPE ty_certattr. 207 | 208 | CONCATENATE LINES OF certificate INTO DATA(certb64). 209 | CONDENSE certb64 NO-GAPS. 210 | 211 | " Remove Header and Footer 212 | TRY. 213 | FIND REGEX '-{5}.{0,}BEGIN.{0,}-{5}(.*)-{5}.{0,}END.{0,}-{5}' IN certb64 SUBMATCHES DATA(base64) ##REGEX_POSIX. 214 | IF sy-subrc = 0. 215 | ASSIGN base64 TO FIELD-SYMBOL(). 216 | ASSERT sy-subrc = 0. 217 | ELSE. 218 | RAISE EXCEPTION TYPE /apmg/cx_error_text EXPORTING text = 'Inconsistent certificate format'(010). 219 | ENDIF. 220 | CATCH cx_sy_regex_too_complex. 221 | " e.g. multiple PEM frames in file 222 | RAISE EXCEPTION TYPE /apmg/cx_error_text EXPORTING text = 'Inconsistent certificate format'(010). 223 | ENDTRY. 224 | 225 | TRY. 226 | DATA(certobj) = NEW cl_abap_x509_certificate( ). 227 | 228 | cert_new-certificate = certobj->get_certificate( ). 229 | 230 | CALL FUNCTION 'SSFC_PARSE_CERTIFICATE' 231 | EXPORTING 232 | certificate = cert_new-certificate 233 | IMPORTING 234 | subject = cert_new-subject 235 | issuer = cert_new-issuer 236 | serialno = cert_new-serialno 237 | validfrom = cert_new-validfrom 238 | validto = cert_new-validto 239 | EXCEPTIONS 240 | ssf_krn_error = 1 241 | ssf_krn_nomemory = 2 242 | ssf_krn_nossflib = 3 243 | ssf_krn_invalid_par = 4 244 | OTHERS = 5. 245 | IF sy-subrc <> 0. 246 | _unlock( ). 247 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 248 | ENDIF. 249 | 250 | cert_new-date_from = cert_new-validfrom(8). 251 | cert_new-date_to = cert_new-validto(8). 252 | APPEND cert_new TO certs_new. 253 | 254 | CATCH cx_abap_x509_certificate. 255 | _unlock( ). 256 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 257 | ENDTRY. 258 | 259 | result = me. 260 | 261 | ENDMETHOD. 262 | 263 | 264 | METHOD add_pem. 265 | 266 | DATA certificate TYPE ty_certificate. 267 | 268 | SPLIT pem AT |\n| INTO TABLE certificate. 269 | 270 | add( certificate ). 271 | 272 | result = me. 273 | 274 | ENDMETHOD. 275 | 276 | 277 | METHOD constructor. 278 | 279 | DATA profile TYPE localfile. 280 | 281 | me->context = context. 282 | me->applic = application. 283 | profilepw = password. 284 | 285 | CALL FUNCTION 'SSFPSE_FILENAME' 286 | EXPORTING 287 | context = context 288 | applic = applic 289 | IMPORTING 290 | psename = psename 291 | psetext = psetext 292 | distrib = distrib 293 | profile = profile 294 | EXCEPTIONS 295 | pse_not_found = 1 296 | OTHERS = 2. 297 | IF sy-subrc <> 0. 298 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 299 | ENDIF. 300 | 301 | me->profile = profile. 302 | 303 | ENDMETHOD. 304 | 305 | 306 | METHOD create. 307 | 308 | result = NEW #( 309 | context = context 310 | application = application 311 | password = password ). 312 | 313 | ENDMETHOD. 314 | 315 | 316 | METHOD get_certificate_list. 317 | 318 | DATA: 319 | certlist TYPE ssfbintab, 320 | certificate TYPE ty_certattr. 321 | 322 | _profile( ). 323 | 324 | CALL FUNCTION 'SSFC_GET_CERTIFICATELIST' 325 | EXPORTING 326 | profile = profile 327 | profilepw = profilepw 328 | IMPORTING 329 | certificatelist = certlist 330 | EXCEPTIONS 331 | ssf_krn_error = 1 332 | ssf_krn_nomemory = 2 333 | ssf_krn_nossflib = 3 334 | ssf_krn_invalid_par = 4 335 | ssf_krn_nocertificate = 5 336 | OTHERS = 6. 337 | IF sy-subrc <> 0. 338 | _unlock( ). 339 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 340 | ENDIF. 341 | 342 | LOOP AT certlist ASSIGNING FIELD-SYMBOL(). 343 | 344 | CLEAR certificate. 345 | 346 | CALL FUNCTION 'SSFC_PARSE_CERTIFICATE' 347 | EXPORTING 348 | certificate = 349 | IMPORTING 350 | subject = certificate-subject 351 | issuer = certificate-issuer 352 | serialno = certificate-serialno 353 | validfrom = certificate-validfrom 354 | validto = certificate-validto 355 | EXCEPTIONS 356 | ssf_krn_error = 1 357 | ssf_krn_nomemory = 2 358 | ssf_krn_nossflib = 3 359 | ssf_krn_invalid_par = 4 360 | OTHERS = 5. 361 | IF sy-subrc <> 0. 362 | _unlock( ). 363 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 364 | ENDIF. 365 | 366 | certificate-date_from = certificate-validfrom(8). 367 | certificate-date_to = certificate-validto(8). 368 | APPEND certificate TO certs_current. 369 | 370 | ENDLOOP. 371 | 372 | result = certs_current. 373 | 374 | ENDMETHOD. 375 | 376 | 377 | METHOD get_own_certificate. 378 | 379 | _profile( ). 380 | 381 | CALL FUNCTION 'SSFC_GET_OWNCERTIFICATE' 382 | EXPORTING 383 | profile = profile 384 | profilepw = profilepw 385 | IMPORTING 386 | certificate = cert_own 387 | EXCEPTIONS 388 | ssf_krn_error = 1 389 | ssf_krn_nomemory = 2 390 | ssf_krn_nossflib = 3 391 | ssf_krn_invalid_par = 4 392 | ssf_krn_nocertificate = 5 393 | OTHERS = 6. 394 | IF sy-subrc <> 0. 395 | _unlock( ). 396 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 397 | ENDIF. 398 | 399 | CALL FUNCTION 'SSFC_PARSE_CERTIFICATE' 400 | EXPORTING 401 | certificate = cert_own 402 | IMPORTING 403 | subject = cert_current-subject 404 | issuer = cert_current-issuer 405 | serialno = cert_current-serialno 406 | validfrom = cert_current-validfrom 407 | validto = cert_current-validto 408 | EXCEPTIONS 409 | ssf_krn_error = 1 410 | ssf_krn_nomemory = 2 411 | ssf_krn_nossflib = 3 412 | ssf_krn_invalid_par = 4 413 | OTHERS = 5. 414 | IF sy-subrc <> 0. 415 | _unlock( ). 416 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 417 | ENDIF. 418 | 419 | cert_current-date_from = cert_current-validfrom(8). 420 | cert_current-date_to = cert_current-validto(8). 421 | 422 | result = cert_current. 423 | 424 | ENDMETHOD. 425 | 426 | 427 | METHOD load. 428 | 429 | CLEAR: is_dirty, certs_removed, certs_current. 430 | 431 | _lock( ). 432 | 433 | CALL FUNCTION 'SSFPSE_LOAD' 434 | EXPORTING 435 | psename = psename 436 | IMPORTING 437 | id = me->id 438 | fname = tempfile 439 | EXCEPTIONS 440 | authority_missing = 1 441 | database_failed = 2 442 | file_write_failed = 3 443 | OTHERS = 4. 444 | IF sy-subrc <> 0. 445 | IF create = abap_true. 446 | _create( 447 | id = id 448 | org = org ). 449 | ELSE. 450 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 451 | ENDIF. 452 | ENDIF. 453 | 454 | result = me. 455 | 456 | ENDMETHOD. 457 | 458 | 459 | METHOD remove. 460 | 461 | _profile( ). 462 | 463 | " Remove certificates 464 | LOOP AT certs_current ASSIGNING FIELD-SYMBOL() WHERE subject = subject. 465 | 466 | CALL FUNCTION 'SSFC_REMOVECERTIFICATE' 467 | EXPORTING 468 | profile = profile 469 | profilepw = profilepw 470 | subject = -subject 471 | issuer = -issuer 472 | serialno = -serialno 473 | EXCEPTIONS 474 | ssf_krn_error = 1 475 | ssf_krn_nomemory = 2 476 | ssf_krn_nossflib = 3 477 | ssf_krn_invalid_par = 4 478 | ssf_krn_nocertificate = 5 479 | OTHERS = 6. 480 | IF sy-subrc <> 0. 481 | _unlock( ). 482 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 483 | ENDIF. 484 | 485 | _log_add( 486 | subject = -subject 487 | issuer = -issuer 488 | date_from = -date_from 489 | date_to = -date_to 490 | status = icon_led_green 491 | message = 'Removed' ). 492 | 493 | is_dirty = abap_true. 494 | 495 | ENDLOOP. 496 | 497 | _save( ). 498 | 499 | _log_save( comment ). 500 | 501 | result = me. 502 | 503 | ENDMETHOD. 504 | 505 | 506 | METHOD update. 507 | 508 | CLEAR certs_removed. 509 | 510 | _profile( ). 511 | 512 | " Remove expired certificates 513 | IF remove_expired = abap_true. 514 | LOOP AT certs_current ASSIGNING FIELD-SYMBOL(). 515 | 516 | LOOP AT certs_new ASSIGNING FIELD-SYMBOL() WHERE subject = -subject. 517 | DATA(tabix) = sy-tabix. 518 | 519 | IF -date_to > -date_to. 520 | " Certificate is newer, so remove the old certificate 521 | CALL FUNCTION 'SSFC_REMOVECERTIFICATE' 522 | EXPORTING 523 | profile = profile 524 | profilepw = profilepw 525 | subject = -subject 526 | issuer = -issuer 527 | serialno = -serialno 528 | EXCEPTIONS 529 | ssf_krn_error = 1 530 | ssf_krn_nomemory = 2 531 | ssf_krn_nossflib = 3 532 | ssf_krn_invalid_par = 4 533 | ssf_krn_nocertificate = 5 534 | OTHERS = 6. 535 | IF sy-subrc <> 0. 536 | _unlock( ). 537 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 538 | ENDIF. 539 | 540 | " Track removed certificate 541 | APPEND TO certs_removed. 542 | 543 | _log_add( 544 | subject = -subject 545 | issuer = -issuer 546 | date_from = -date_from 547 | date_to = -date_to 548 | status = icon_led_green 549 | message = 'Removed' ). 550 | 551 | is_dirty = abap_true. 552 | ELSE. 553 | " Certificate already exists, no update necessary 554 | DELETE certs_new INDEX tabix. 555 | ENDIF. 556 | 557 | ENDLOOP. 558 | 559 | ENDLOOP. 560 | ENDIF. 561 | 562 | " Add new certificates 563 | LOOP AT certs_new ASSIGNING . 564 | 565 | CALL FUNCTION 'SSFC_PUT_CERTIFICATE' 566 | EXPORTING 567 | profile = profile 568 | profilepw = profilepw 569 | certificate = -certificate 570 | EXCEPTIONS 571 | ssf_krn_error = 1 572 | ssf_krn_nomemory = 2 573 | ssf_krn_nossflib = 3 574 | ssf_krn_invalid_par = 4 575 | ssf_krn_certexists = 5 576 | OTHERS = 6. 577 | 578 | IF sy-subrc = 5. 579 | " SAP message is misleading so use a better text 580 | _unlock( ). 581 | RAISE EXCEPTION TYPE /apmg/cx_error_text 582 | EXPORTING 583 | text = |Certificate { -subject } already exists|. 584 | ELSEIF sy-subrc <> 0. 585 | _unlock( ). 586 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 587 | ENDIF. 588 | 589 | _log_add( 590 | subject = -subject 591 | issuer = -issuer 592 | date_from = -date_from 593 | date_to = -date_to 594 | status = icon_led_green 595 | message = 'Added' ). 596 | 597 | is_dirty = abap_true. 598 | 599 | ENDLOOP. 600 | 601 | _save( ). 602 | 603 | _log_save( comment ). 604 | 605 | result-added = certs_new. 606 | result-removed = certs_removed. 607 | 608 | ENDMETHOD. 609 | 610 | 611 | METHOD _create. 612 | 613 | DATA: 614 | license_num TYPE c LENGTH 10, 615 | new_id TYPE ssfid, 616 | subject TYPE certsubjct, 617 | psepath TYPE trfile. 618 | 619 | " Create new PSE (using RSA-SHA256 2048 which is the default in STRUST in recent releases) 620 | IF id IS INITIAL. 621 | CASE applic. 622 | WHEN 'DFAULT'. 623 | new_id = `CN=%SID SSL client SSL Client (Standard), ` && 624 | `OU=I%LIC, OU=SAP Web AS, O=SAP Trust Community, C=DE` ##NO_TEXT. 625 | WHEN 'ANONYM'. 626 | new_id = 'CN=anonymous' ##NO_TEXT. 627 | ENDCASE. 628 | ELSE. 629 | new_id = id. 630 | ENDIF. 631 | 632 | CALL FUNCTION 'SLIC_GET_LICENCE_NUMBER' 633 | IMPORTING 634 | license_number = license_num. 635 | 636 | REPLACE '%SID' IN new_id WITH sy-sysid. 637 | REPLACE '%LIC' IN new_id WITH license_num. 638 | REPLACE '%ORG' IN new_id WITH org. 639 | CONDENSE new_id. 640 | 641 | subject = new_id. 642 | 643 | CALL FUNCTION 'SSFPSE_CREATE' 644 | EXPORTING 645 | dn = subject 646 | alg = 'R' 647 | keylen = 2048 648 | IMPORTING 649 | psepath = psepath 650 | EXCEPTIONS 651 | ssf_unknown_error = 1 652 | OTHERS = 2. 653 | IF sy-subrc <> 0. 654 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 655 | ENDIF. 656 | 657 | tempfile = psepath. 658 | 659 | _save( ). 660 | 661 | ENDMETHOD. 662 | 663 | 664 | METHOD _lock. 665 | 666 | CALL FUNCTION 'SSFPSE_ENQUEUE' 667 | EXPORTING 668 | psename = psename 669 | EXCEPTIONS 670 | database_failed = 1 671 | foreign_lock = 2 672 | internal_error = 3 673 | OTHERS = 4. 674 | IF sy-subrc <> 0. 675 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 676 | ENDIF. 677 | 678 | ENDMETHOD. 679 | 680 | 681 | METHOD _log_add. 682 | 683 | DATA(log) = VALUE /apmg/strust_log( 684 | cert_subject = subject 685 | cert_issues = issuer 686 | date_from = date_from 687 | date_to = date_to 688 | status = status 689 | message = message ). 690 | 691 | INSERT log INTO TABLE logs. 692 | 693 | ENDMETHOD. 694 | 695 | 696 | METHOD _log_save. 697 | 698 | GET TIME STAMP FIELD DATA(timestamp). 699 | 700 | LOOP AT logs ASSIGNING FIELD-SYMBOL(). 701 | -timestamp = timestamp. 702 | -counter = sy-tabix. 703 | -username = sy-uname. 704 | -comments = comment. 705 | ENDLOOP. 706 | 707 | INSERT /apmg/strust_log FROM TABLE @logs. 708 | IF sy-subrc <> 0. 709 | RAISE EXCEPTION TYPE /apmg/cx_error_text EXPORTING text = 'Error saving comments to log table'(012). 710 | ENDIF. 711 | 712 | ENDMETHOD. 713 | 714 | 715 | METHOD _profile. 716 | 717 | IF tempfile IS NOT INITIAL. 718 | profile = tempfile. 719 | ENDIF. 720 | 721 | IF profile IS INITIAL. 722 | RAISE EXCEPTION TYPE /apmg/cx_error_text EXPORTING text = 'Missing profile. Call "load" first'(011). 723 | ENDIF. 724 | 725 | ENDMETHOD. 726 | 727 | 728 | METHOD _save. 729 | 730 | DATA cred_name TYPE icm_credname. 731 | 732 | CHECK is_dirty = abap_true. 733 | 734 | " Store PSE 735 | CALL FUNCTION 'SSFPSE_STORE' 736 | EXPORTING 737 | fname = tempfile 738 | psepin = profilepw 739 | psename = psename 740 | id = id 741 | b_newdn = abap_false 742 | b_distribute = distrib 743 | EXCEPTIONS 744 | file_load_failed = 1 745 | storing_failed = 2 746 | authority_missing = 3 747 | OTHERS = 4. 748 | IF sy-subrc <> 0. 749 | _unlock( ). 750 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 751 | ENDIF. 752 | 753 | IF profile(3) = 'SSL'. 754 | cred_name = psename. 755 | 756 | CALL FUNCTION 'ICM_SSL_PSE_CHANGED' 757 | EXPORTING 758 | global = 1 759 | cred_name = cred_name 760 | EXCEPTIONS 761 | icm_op_failed = 1 762 | icm_get_serv_failed = 2 763 | icm_auth_failed = 3 764 | OTHERS = 4. 765 | IF sy-subrc = 0. 766 | MESSAGE s086(trust). 767 | ELSE. 768 | MESSAGE s085(trust). 769 | ENDIF. 770 | ELSE. 771 | MESSAGE 'Certificate was saved successfully' TYPE 'S'. 772 | ENDIF. 773 | 774 | _unlock( ). 775 | 776 | ENDMETHOD. 777 | 778 | 779 | METHOD _unlock. 780 | 781 | " Drop temporary file 782 | TRY. 783 | DELETE DATASET tempfile. 784 | CATCH cx_sy_file_open. 785 | RAISE EXCEPTION TYPE /apmg/cx_error_text 786 | EXPORTING 787 | text = 'Error deleting file'(020) && | { tempfile }|. 788 | CATCH cx_sy_file_authority. 789 | RAISE EXCEPTION TYPE /apmg/cx_error_text 790 | EXPORTING 791 | text = 'Not authorized to delete file'(030) && | { tempfile }|. 792 | ENDTRY. 793 | 794 | " Unlock PSE 795 | CALL FUNCTION 'SSFPSE_DEQUEUE' 796 | EXPORTING 797 | psename = psename 798 | EXCEPTIONS 799 | database_failed = 1 800 | foreign_lock = 2 801 | internal_error = 3 802 | OTHERS = 4. 803 | IF sy-subrc <> 0. 804 | RAISE EXCEPTION TYPE /apmg/cx_error_t100. 805 | ENDIF. 806 | 807 | ENDMETHOD. 808 | ENDCLASS. 809 | --------------------------------------------------------------------------------