{"id":37020785,"url":"https://github.com/nabhosal/pii-encryptor","last_synced_at":"2026-01-14T02:25:46.278Z","repository":{"id":57734272,"uuid":"206777718","full_name":"nabhosal/pii-encryptor","owner":"nabhosal","description":"Utility for encrypting \u0026 decryption field level PII (i.e Personally identifiable information) in document. Useful utility for implementing in-house Payment Card Industry ( PCI ) or similar  compliance.","archived":false,"fork":false,"pushed_at":"2019-09-20T12:21:58.000Z","size":48,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-02-14T22:31:38.745Z","etag":null,"topics":["csv","field-level-encryption","json","pci","pci-dss","utility-library"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nabhosal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-06T11:15:26.000Z","updated_at":"2024-02-14T22:31:38.747Z","dependencies_parsed_at":"2022-09-26T22:10:59.640Z","dependency_job_id":null,"html_url":"https://github.com/nabhosal/pii-encryptor","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nabhosal/pii-encryptor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabhosal%2Fpii-encryptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabhosal%2Fpii-encryptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabhosal%2Fpii-encryptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabhosal%2Fpii-encryptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nabhosal","download_url":"https://codeload.github.com/nabhosal/pii-encryptor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nabhosal%2Fpii-encryptor/sbom","scorecard":{"id":672414,"data":{"date":"2025-08-11","repo":{"name":"github.com/nabhosal/pii-encryptor","commit":"52e70f1c492b42e2a0ed48e9ec56d9b977ba4c7a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/5 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T20:36:14.560Z","repository_id":57734272,"created_at":"2025-08-21T20:36:14.560Z","updated_at":"2025-08-21T20:36:14.560Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csv","field-level-encryption","json","pci","pci-dss","utility-library"],"created_at":"2026-01-14T02:25:45.717Z","updated_at":"2026-01-14T02:25:46.270Z","avatar_url":"https://github.com/nabhosal.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PII Encryptor \n\nIt is a utility project to help implement field-level encryption on the csv or json document. It is designed to be used by service which needs PCI or similar compliance. It preloaded with a default implementation for csv and json, it simplifies indicating fields using Jsonpath for json and field index in case of csv. \nPreloaded class are designed to take care of most common implementation challenges, making developer focus on encryption strategy.  \n\n##### Get it as Maven dependency\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.github.nabhosal\u003c/groupId\u003e\n  \u003cartifactId\u003eapp-security\u003c/artifactId\u003e\n  \u003cversion\u003e1.0-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\nexternal dependencies it used (All are authentic \u0026 secure libraries)\n```groovy\n    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9'\n    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.9'\n    compile group: 'com.jayway.jsonpath', name: 'json-path', version: '2.4.0'\n    compile group: 'commons-codec', name: 'commons-codec', version: '1.13'\n    compile group: 'org.apache.commons', name: 'commons-csv', version: '1.7'\n```\n\n### Basic terminologies\n \n* **Field-level encryption:** focus on encrypting data at specified fields only.\n* **Data encryption key (DEK):** is an encryption key whose function it is to encrypt and decrypt the data.\n* **Key encryption key (KEK):** is an encryption key whose function it is to encrypt and decrypt the DEK.\n* **Encrypted data encryption key (eDEK):** a shareable DEK encrypted using KEK\n* **Key Management System (KMS):** is the system that houses the key management\n#### Implementation terminologies\n* **Codec:** a metadata detecting the strategy usable for encrypting or hashing a given fields\n* **CodecLoader:** codec provider from external sources such as db, or through java class\n* **EncryptionService:** a AES-256 based encryption service for encrypting \u0026 decrypting the data, \nit relies on creating new encryption session for encrypting and rebuilding the encryption session while decrypting.\n* **Key Provider:** KEK key list provider from external sources such as db, files, or KMS\n\n### Implementation philosophy\n1. Encrypted data is self sufficient, it know what strategy(i.e. codec) \u0026 data key (i.e. eDEK) is used to encrypt. \nSince eDEK is in encrypted using different key (i.e KEK), the data contains reference to KEK.\n2. If DEK in single document is compromised, it wont have affect on other document since the DEK are fairly unique to each document\n3. if KEK is compromised, it will compromise only documents where given KEK is being used, **_Key provider_** provide strategy strong \nenough to distribute KEK across multiple document to handle incident of KEK compromises.\n\n### Example\nSample input json containing PII data. \n\nthe fields for encryption is `_pan, mobile, arrays[0].k2, arrays[1].k2_`\n\nthe fields for hashing is `_pan_name, nested.key1, mobile, arrays[0].k1_`\n```json5\n{\n    \"name\":\"full name\",\n    \"pan\":\"123124324\",\n    \"pan_name\":\"pan full name\",\n    \"mobile\":\"4534534534\",\n    \"nested\":{\n        \"key1\":\"value1\",\n        \"key2\":\"value2\"\n    },\n    \"arrays\":[\n        {\n            \"k1\":\"v1\",\n            \"k2\":\"v2\"\n        },\n        {\n            \"k1\":\"v11\",\n            \"k2\":\"v21\"\n        }\n    ]\n}\n```\n#### field-level encrypted data\n\nfor every hash field, a surrogate new field is added starting with `h_` \n\nthe encrypted document contains metadata such as _`codec, DEK, KEKID`_. \n\n```json5\n{\n    \"name\":\"full name\",\n    \"pan\":\"H+msxmkCaSvTFLTIYZDTUw==\",\n    \"pan_name\":\"pan full name\",\n    \"mobile\":\"5Hp8tjD10rdwlJrMoM1qCw==\",\n    \"nested\":{\n        \"key1\":\"value1\",\n        \"key2\":\"value2\",\n        \"h_key1\":\"3c9683017f9e4bf33d0fbedd26bf143fd72de9b9dd145441b75f0604047ea28e\"\n    },\n    \"arrays\":[\n        {\n            \"k1\":\"v1\",\n            \"k2\":\"QcuAVmE/hvy4zQG/Vg7VNg==\",\n            \"h_k1\":\"3bfc269594ef649228e9a74bab00f042efc91d5acc6fbee31a382e80d42388fe\"\n        },\n        {\n            \"k1\":\"v11\",\n            \"k2\":\"UwGk6VAHJl8dZDVKMpeUAw==\"\n        }\n    ],\n    \"h_pan_name\":\"8766d7e0b05c3fd0e62307e4a4551999a8308d411b24b8539afea0a4e42ab006\",\n    \"h_mobile\":\"ccbf71ae2de17bcea3950fdd0b0cd2f47b6901f7244267324088d7f914c068c5\",\n    \"codec\":\"test-code-imp\",\n    \"DEK\":\"2/vGlA+G+hgFMzAWvlJOZiwoQMWXyHnjL3Faeop5Xt4=\",\n    \"KEKID\":\"1d3ca4c3-44bc-4c61-944c-f82b591787fa\"\n}\n```\n\n### How to use API\nthe standard method will return PIIHandler with default json implementation  _`JsonBasedStandardCodec`_ for json codec, _`DemoJsonCodecLoader`_ for codecloader, _`MapBasedKeyProviderImpl`_ for keyProvider.\n```java\n                PIIHandler piiHandler = PIIHandlerBuilder.standard();\n                String encrypteddata = piiHandler.apply(input_json, \"test-code-imp\");\n                String decrypteddata = piiHandler.resolve(encrypteddata);\n        \n                System.out.println(\"orginaldata \"+input_json);\n                System.out.println(\"encrypteddata \"+encrypteddata);\n                System.out.println(\"orginaldata \"+input_json);\n            \n```\n#### JsonBasedStandardCodec used in example\n```java\n        JsonBasedStandardCodec codec = new JsonBasedStandardCodec();\n        codec.encrypt(\"$.pan\");\n        codec.addHash(\"$.pan_name\");\n        codec.addHash(\"$.nested.key1\");\n        codec.encrytWithHash(\"$.mobile\");\n        codec.addHash(\"$.arrays[0].k1\");\n        codec.encrypt(\"$.arrays[*].k2\");\n        codec.setCode(\"test-code-imp\");\n        codec.setCodecType(DEFAULT_CODECTYPE);\n```\n#### Result\n```commandline\norginaldata {\"name\":\"full name\",\"pan\":\"123124324\",\"pan_name\":\"pan full name\",\"mobile\":\"4534534534\",\"hello\":\"Hash\",\"nested\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"arrays\":[{\"k1\":\"v1\",\"k2\":\"v2\"},{\"k1\":\"v11\",\"k2\":\"v21\"}]}\nencrypteddata {\"name\":\"full name\",\"pan\":\"kPNOioYbrsw/64s0df4n+A==\",\"pan_name\":\"pan full name\",\"mobile\":\"WuEsq5E6a9SW/V4kyNRI5A==\",\"hello\":\"Hash\",\"nested\":{\"key1\":\"value1\",\"key2\":\"value2\",\"h_key1\":\"3c9683017f9e4bf33d0fbedd26bf143fd72de9b9dd145441b75f0604047ea28e\"},\"arrays\":[{\"k1\":\"v1\",\"k2\":\"69jco+QP0+hmJNsmGhCThg==\",\"h_k1\":\"3bfc269594ef649228e9a74bab00f042efc91d5acc6fbee31a382e80d42388fe\"},{\"k1\":\"v11\",\"k2\":\"/u/Wb9ZKgjav6dv9Qmz4uw==\"}],\"h_pan_name\":\"8766d7e0b05c3fd0e62307e4a4551999a8308d411b24b8539afea0a4e42ab006\",\"h_mobile\":\"ccbf71ae2de17bcea3950fdd0b0cd2f47b6901f7244267324088d7f914c068c5\",\"codec\":\"test-code-imp\",\"DEK\":\"mOoWkgCO58yXFbK1E6Llu0iuiX/g8mU/bJscH+bldLQ=\",\"KEKID\":\"0d50f9aa-6436-4cdd-b88d-a23d8cdd90b7\"}\ndecrypteddata {\"name\":\"full name\",\"pan\":\"123124324\",\"pan_name\":\"pan full name\",\"mobile\":\"4534534534\",\"hello\":\"Hash\",\"nested\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"arrays\":[{\"k1\":\"v1\",\"k2\":\"v2\"},{\"k1\":\"v11\",\"k2\":\"v21\"}]}\n\n```\n\n#### How to apply field-level encryption on CSV\n```java\n/* csv data */\nprivate static String input_csv = \"Rajeev Kumar Singh,\\\"rajeevs@example.com\\\",+91-9999999999,India\\n\" +\n            \"Sachin Tendulkar,sachin@example.com,+91-9999999998,India\\n\" +\n            \"Barak Obama,barak.obama@example.com,+1-1111111111,United States\\n\" +\n            \"Donald Trump,donald.trump@example.com,+1-2222222222,United States\";\n\n/* Codec */\nCSVByFieldIndexStandardCodec c1 = new CSVByFieldIndexStandardCodec();\n            c1.addHash(2);\n            c1.encrypt(0);\n            c1.setCode(\"test-01\");\n            \n/* Encrypt \u0026 decrypt the csv field level data */\n\n PIIHandler piiHandler = PIIHandlerBuilder.withDefault()\n                .withCodecLoader(new DemoCSVCodecLoader()).build();\n        String encrypteddata = piiHandler.apply(input_csv, \"test-01\");\n        String decrypteddata = piiHandler.resolve(encrypteddata);\n        assertNotEquals(\"input csv is not same after decryption\", decrypteddata, input_csv);\n\n/* for more details refer TestCSVCodec.java */\n```\n\n#### Working with custom implementation\n\n\u003cdetails\u003e\u003csummary\u003eCustom implementation to handle CSV \u003c/summary\u003e\n\u003cp\u003e\n\n```java\npackage io.github.nabhosal.pii.test;\n\nimport io.github.nabhosal.pii.PIIHandler;\nimport io.github.nabhosal.pii.PIIHandlerBuilder;\nimport io.github.nabhosal.pii.cipher.EncryptionService;\nimport io.github.nabhosal.pii.encoder.Codec;\nimport io.github.nabhosal.pii.encoder.CodecLoader;\nimport io.github.nabhosal.pii.encoder.impl.DemoCSVCodecLoader;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.csv.CSVFormat;\nimport org.apache.commons.csv.CSVParser;\nimport org.apache.commons.csv.CSVPrinter;\nimport org.apache.commons.csv.CSVRecord;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.util.*;\n\nimport static org.junit.Assert.assertNotEquals;\n\npublic class TestCSVCodec {\n\n    private static String input_csv = \"Rajeev Kumar Singh,\\\"rajeevs@example.com\\\",+91-9999999999,India\\n\" +\n            \"Sachin Tendulkar,sachin@example.com,+91-9999999998,India\\n\" +\n            \"Barak Obama,barak.obama@example.com,+1-1111111111,United States\\n\" +\n            \"Donald Trump,donald.trump@example.com,+1-2222222222,United States\";\n\n    \n    @Test\n    public void testCustomCSVbyFieldImpl(){\n        PIIHandler piiHandler = PIIHandlerBuilder.withDefault().withCodecLoader(new CSVByFieldIndexCodecLoader()).build();\n        String encrypteddata = piiHandler.apply(input_csv, \"test-01\");\n        String decrypteddata = piiHandler.resolve(encrypteddata);\n        assertNotEquals(\"input csv is not same after decryption\", decrypteddata, input_csv);\n    }\n\n    static class CSVByFieldIndexCodecLoader implements CodecLoader {\n\n        private final HashMap\u003cString, Codec\u003e codecMap = new HashMap\u003c\u003e();\n\n        public CSVByFieldIndexCodecLoader(){\n            CSVByFieldIndexStandardCodec c1 = new CSVByFieldIndexStandardCodec();\n            c1.addHash(2);\n            c1.encrypt(0);\n            c1.setCode(\"test-01\");\n\n            codecMap.put(c1.getCode(), c1);\n        }\n\n\n        @Override\n        public Codec loadByCode(String code) {\n            return codecMap.get(code);\n        }\n\n        @Override\n        public String infer(String cipher) {\n\n            CSVParser csvParser = null;\n            String code = \"\";\n\n            try {\n                csvParser = CSVParser.parse(cipher, CSVFormat.DEFAULT.withSkipHeaderRecord());\n                CSVRecord first = csvParser.getRecords().get(0);\n                code = first.get(first.size() - 1);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n            if (\"\".equalsIgnoreCase(code))\n                return cipher;\n\n            return code;\n        }\n    }\n\n    static class CSVByFieldIndexStandardCodec implements Codec\u003cInteger\u003e{\n\n        private String code;\n        private String codecType;\n        public static final String DEFAULT_CODECTYPE = \"csv_byindex_01\";\n        private Set\u003cInteger\u003e efields;\n        private Set\u003cInteger\u003e hfields;\n\n        public CSVByFieldIndexStandardCodec(){\n            efields = new LinkedHashSet\u003c\u003e();\n            hfields = new LinkedHashSet\u003c\u003e();\n        }\n\n        @Override\n        public String getCode() {\n            return code;\n        }\n\n        @Override\n        public String apply(String rawdata, EncryptionService encryptionService) {\n\n            EncryptionService.EncryptionSession session = encryptionService.newSession();\n\n            CSVParser csvParser = null;\n            StringWriter csvInString = new StringWriter();\n            CSVPrinter writer = null;\n\n            try {\n                csvParser = CSVParser.parse(rawdata, CSVFormat.DEFAULT.withSkipHeaderRecord());\n                writer = new CSVPrinter(csvInString, CSVFormat.DEFAULT.withSkipHeaderRecord());\n                for (CSVRecord csvRecord : csvParser) {\n                    int totalFields = csvRecord.size();\n                    List\u003cString\u003e list = new ArrayList\u003cString\u003e(totalFields + hfields.size() + 3);\n\n                    for(String field : csvRecord)\n                        list.add(field);\n\n                    for(int fieldIndex : hfields)\n                        list.add(DigestUtils.sha256Hex(list.get(fieldIndex)));\n\n                    for(int fieldIndex : efields)\n                        list.set(fieldIndex, encryptionService.encrypt(session, list.get(fieldIndex)));\n\n                    list.add(session.geteDEK());\n                    list.add(session.getKEKId());\n                    list.add(DEFAULT_CODECTYPE);\n                    list.add(getCode());\n\n                    writer.printRecord(list);\n                }\n\n                writer.close(true);\n                return csvInString.toString();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n            return rawdata;\n        }\n\n        @Override\n        public String resolve(String cipher, EncryptionService encryptionService) {\n\n            EncryptionService.EncryptionSession session = null;\n\n            CSVParser csvParser = null;\n            StringWriter csvInString = new StringWriter();\n            CSVPrinter writer = null;\n\n            try {\n                csvParser = CSVParser.parse(cipher, CSVFormat.DEFAULT.withSkipHeaderRecord());\n                writer = new CSVPrinter(csvInString, CSVFormat.DEFAULT.withSkipHeaderRecord());\n\n                CSVParser forInfer = CSVParser.parse(cipher, CSVFormat.DEFAULT.withSkipHeaderRecord());\n                CSVRecord first = forInfer.getRecords().get(0);\n                String eDEK = first.get(first.size() - 4);\n                String KEKId = first.get(first.size() - 3);\n\n                session = encryptionService.buildSession(eDEK, KEKId);\n\n                for (CSVRecord csvRecord : csvParser) {\n\n                    int totalFields = csvRecord.size() - hfields.size() - 3 - 1;\n\n                    List\u003cString\u003e list = new ArrayList\u003cString\u003e(csvRecord.size());\n                    for(int i = 0; i \u003c totalFields ; i++){\n\n                        if( efields.contains(i)){\n                            list.add(encryptionService.decrypt(session, csvRecord.get(i)));\n                        }else\n                            list.add(csvRecord.get(i));\n                    }\n\n                    writer.printRecord(list);\n                }\n\n                writer.close(true);\n                return csvInString.toString();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n            return cipher;\n        }\n\n        @Override\n        public Codec encrypt(Integer field) {\n            efields.add(Integer.valueOf(field));\n            return this;\n        }\n\n        @Override\n        public Codec addHash(Integer field) {\n            hfields.add(Integer.valueOf(field));\n            return this;\n        }\n\n        @Override\n        public Codec encrytWithHash(Integer field) {\n            return encrypt(field).addHash(field);\n        }\n\n        @Override\n        public Codec setCode(String code) {\n            this.code = code;\n            return this;\n        }\n    }\n\n}\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eBuilding PIIHandler with custom CodecLoader \u0026 EncryptionService \u003c/summary\u003e\n\u003cp\u003e\n\n```java\npackage io.github.nabhosal.pii.test;\n\nimport io.github.nabhosal.pii.PIIHandler;\nimport io.github.nabhosal.pii.cipher.KeyProvider;\nimport io.github.nabhosal.pii.cipher.impl.AESBasedEncryptionService;\nimport io.github.nabhosal.pii.cipher.EncryptionService;\nimport io.github.nabhosal.pii.cipher.impl.MapBasedKeyProviderImpl;\nimport io.github.nabhosal.pii.encoder.Codec;\nimport io.github.nabhosal.pii.encoder.CodecLoader;\nimport io.github.nabhosal.pii.encoder.impl.JsonBasedStandardCodec;\nimport io.github.nabhosal.pii.encoder.impl.DemoJsonCodecLoader;\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.github.nabhosal.pii.PIIHandlerBuilder;\nimport org.junit.Test;\n\nimport java.io.*;\nimport java.util.*;\n\nimport static io.github.nabhosal.pii.encoder.impl.JsonBasedStandardCodec.DEFAULT_CODECTYPE;\nimport static junit.framework.TestCase.assertFalse;\nimport static org.junit.Assert.assertNotEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class TestJsonCodec {\n\n    private static String input_json = \"{\\\"name\\\":\\\"full name\\\",\\\"pan\\\":\\\"123124324\\\",\\\"pan_name\\\":\\\"pan full name\\\",\\\"mobile\\\":\\\"4534534534\\\",\\\"hello\\\":\\\"Hash\\\",\\\"nested\\\":{\\\"key1\\\":\\\"value1\\\",\\\"key2\\\":\\\"value2\\\"},\\\"arrays\\\":[{\\\"k1\\\":\\\"v1\\\",\\\"k2\\\":\\\"v2\\\"},{\\\"k1\\\":\\\"v11\\\",\\\"k2\\\":\\\"v21\\\"}]}\";\n\n    @Test\n    public void testPIIBuilderImpl(){\n\n        PIIHandler piiHandler = PIIHandlerBuilder.withDefault()\n                .withCodecLoader(new TestCodecLoader())\n                .withEncryptionService(new TestEncryptionService())\n                .build();\n\n        String encrypteddata = piiHandler.apply(input_json, \"test-codec\");\n        String decrypteddata = piiHandler.resolve(encrypteddata);\n\n        assertFalse(\"decrypteddata should not be same as orginaldata due to hardcoded value encryptionService\", input_json.equalsIgnoreCase(decrypteddata));\n\n    }\n\n    static class TestCodecLoader implements CodecLoader{\n\n        @Override\n        public Codec loadByCode(String code) {\n\n            JsonBasedStandardCodec codec = new JsonBasedStandardCodec();\n            codec.encrypt(\"$.pan\");\n            codec.setCode(\"test-codec\");\n            codec.setCodecType(DEFAULT_CODECTYPE);\n\n            return codec;\n        }\n\n        @Override\n        public String infer(String cipher) {\n            String codecStr = \"\";\n            try {\n                codecStr = new ObjectMapper().readTree(cipher).get(\"codec\").asText(\"\");\n            } catch (IOException e) {\n                System.out.println(\"DemoJsonCodecLoader: codec field not found\");\n                return cipher;\n            } catch (NullPointerException e){\n                System.out.println(\"DemoJsonCodecLoader: codec field not found\");\n                return cipher;\n            }\n\n            if(\"\".equalsIgnoreCase(codecStr)){\n                return cipher;\n            }\n            return codecStr;\n        }\n    }\n\n    static class TestEncryptionService implements EncryptionService {\n\n\n        @Override\n        public String encrypt(EncryptionSession session, String raw) {\n            return \"encrypt\";\n        }\n\n        @Override\n        public String decrypt(EncryptionSession session, String cipher) {\n            return \"raw\";\n        }\n\n        @Override\n        public KeyProvider getKeyProvider() {\n            return new KeyProvider(){\n\n                HashMap\u003cString, String\u003e map = new HashMap\u003c\u003e();\n\n                @Override\n                public String getKeyById(String id) {\n                    map.put(id, id);\n                    return map.get(id);\n                }\n\n                @Override\n                public String getKeyForEncryption(Map\u003cString, Object\u003e params) {\n\n                    return \"\";\n                }\n            };\n        }\n\n        @Override\n        public EncryptionSession buildSession(String eDEK, String KEKId) {\n            return new TestEncryptionSessionImpl();\n        }\n\n        @Override\n        public EncryptionSession newSession() {\n            return new TestEncryptionSessionImpl();\n        }\n\n        static class TestEncryptionSessionImpl implements EncryptionSession{\n\n            public TestEncryptionSessionImpl(){\n\n            }\n\n            @Override\n            public String getDEK() {\n                return \"dek\";\n            }\n\n            @Override\n            public String geteDEK() {\n                return \"edk\";\n            }\n\n            @Override\n            public String getKEKId() {\n                return \"123\";\n            }\n\n        }\n\n    }\n}\n\n\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnabhosal%2Fpii-encryptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnabhosal%2Fpii-encryptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnabhosal%2Fpii-encryptor/lists"}