├── .gitignore
├── .gitmodules
├── README.md
├── README_DEMO.md
├── admin
├── .gitignore
├── README.md
├── build.gradle
├── config-example
│ ├── commands.json
│ ├── config.properties
│ └── jaas.conf
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── nucypher
│ │ │ └── kafka
│ │ │ └── admin
│ │ │ ├── AdminHandler.java
│ │ │ ├── AdminZooKeeperHandler.java
│ │ │ ├── Console.java
│ │ │ └── databind
│ │ │ ├── Command.java
│ │ │ ├── CommandFactory.java
│ │ │ └── CommandType.java
│ └── resources
│ │ └── log4j2.xml
│ └── test
│ ├── java
│ └── com
│ │ └── nucypher
│ │ └── kafka
│ │ └── admin
│ │ ├── AdminHandlerTest.java
│ │ ├── CommandFactoryTest.java
│ │ └── ZooKeeperHandlerTest.java
│ └── resources
│ ├── command.json
│ ├── commands.json
│ ├── jaas_test.conf
│ └── wrong_commands.json
├── build.gradle
├── build_project.sh
├── clients
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── nucypher
│ │ │ └── kafka
│ │ │ └── clients
│ │ │ ├── MessageSerDeConfig.java
│ │ │ ├── StructuredMessageSerDeConfig.java
│ │ │ ├── decrypt
│ │ │ ├── AesMessageDeserializer.java
│ │ │ ├── AesMessageDeserializerConfig.java
│ │ │ ├── AesStructuredMessageDeserializer.java
│ │ │ └── AesStructuredMessageDeserializerConfig.java
│ │ │ └── encrypt
│ │ │ ├── AesMessageSerializer.java
│ │ │ ├── AesMessageSerializerConfig.java
│ │ │ ├── AesStructuredMessageSerializer.java
│ │ │ └── AesStructuredMessageSerializerConfig.java
│ └── resources
│ │ ├── P521.pem
│ │ ├── consumer.properties
│ │ └── producer.properties
│ └── test
│ └── groovy
│ └── com
│ └── nucypher
│ └── kafka
│ └── clients
│ ├── AesMessageEncryptorDecryptorSpec.groovy
│ └── StructuredMessageSerializeDeserializeSpec.groovy
├── commons
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ ├── com
│ │ └── nucypher
│ │ │ └── kafka
│ │ │ ├── Constants.java
│ │ │ ├── DefaultProvider.java
│ │ │ ├── INamed.java
│ │ │ ├── cipher
│ │ │ ├── CipherFactory.java
│ │ │ ├── ICipher.java
│ │ │ ├── JCECipher.java
│ │ │ └── OpenSSLCipher.java
│ │ │ ├── clients
│ │ │ ├── EncryptedDataEncryptionKey.java
│ │ │ ├── Message.java
│ │ │ ├── MessageHandler.java
│ │ │ ├── ReEncryptionHandler.java
│ │ │ ├── ReEncryptionHandlerConfigs.java
│ │ │ └── granular
│ │ │ │ ├── AbstractAvroDataAccessor.java
│ │ │ │ ├── AvroDataAccessor.java
│ │ │ │ ├── AvroSchemaLessDataAccessor.java
│ │ │ │ ├── DataFormat.java
│ │ │ │ ├── JsonDataAccessor.java
│ │ │ │ ├── OneMessageDataAccessor.java
│ │ │ │ ├── StructuredDataAccessor.java
│ │ │ │ └── StructuredMessageHandler.java
│ │ │ ├── encrypt
│ │ │ ├── DataEncryptionKeyManager.java
│ │ │ └── ReEncryptionKeyManager.java
│ │ │ ├── errors
│ │ │ └── CommonException.java
│ │ │ ├── utils
│ │ │ ├── AESKeyGenerators.java
│ │ │ ├── AvroUtils.java
│ │ │ ├── ByteUtils.java
│ │ │ ├── EncryptionAlgorithmUtils.java
│ │ │ ├── GranularUtils.java
│ │ │ ├── KeyType.java
│ │ │ ├── KeyUtils.java
│ │ │ ├── StringUtils.java
│ │ │ ├── SubkeyGenerator.java
│ │ │ └── WrapperReEncryptionKey.java
│ │ │ └── zk
│ │ │ ├── BaseZooKeeperHandler.java
│ │ │ ├── Channel.java
│ │ │ ├── ClientType.java
│ │ │ ├── EncryptionType.java
│ │ │ └── KeyHolder.java
│ │ └── org
│ │ └── apache
│ │ └── avro
│ │ └── file
│ │ └── GenericDataFileWriter.java
│ └── test
│ ├── groovy
│ └── com
│ │ └── nucypher
│ │ └── kafka
│ │ ├── cipher
│ │ └── CipherSpec.groovy
│ │ └── clients
│ │ ├── MessageHandlerSpec.groovy
│ │ ├── MessageSpec.groovy
│ │ └── granular
│ │ ├── AvroDataAccessorSpec.groovy
│ │ ├── AvroSchemaLessDataAccessorSpec.groovy
│ │ ├── JsonDataAccessorSpec.groovy
│ │ └── StructuredMessageHandlerSpec.groovy
│ ├── java
│ └── com
│ │ └── nucypher
│ │ └── kafka
│ │ ├── TestUtils.java
│ │ ├── clients
│ │ └── granular
│ │ │ ├── AvroTestUtils.java
│ │ │ └── StructuredDataAccessorStub.java
│ │ ├── encrypt
│ │ ├── DataEncryptionKeyManagerTest.java
│ │ └── ReEncryptionKeyManagerTest.java
│ │ ├── utils
│ │ ├── GranularUtilsTest.java
│ │ ├── KeyUtilsAlgorithmTest.java
│ │ ├── KeyUtilsTest.java
│ │ └── SubkeyGeneratorTest.java
│ │ └── zk
│ │ ├── BaseZooKeeperHandlerTest.java
│ │ ├── DataUtils.java
│ │ └── ZooKeeperSASLResource.java
│ └── resources
│ ├── P521.pem
│ ├── jaas_test.conf
│ ├── log4j2.xml
│ ├── private-key-prime256v1-1.pem
│ ├── private-key-prime256v1-2.pem
│ ├── private-key-secp521r1-1.pem
│ ├── private-key-secp521r1-2.pem
│ ├── public-key-prime256v1-1.pem
│ ├── public-key-prime256v1-2.pem
│ ├── public-key-secp521r1-1.pem
│ └── public-key-secp521r1-2.pem
├── dependencies_libs.gradle
├── examples
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── nucypher
│ │ └── kafka
│ │ ├── clients
│ │ └── example
│ │ │ ├── general
│ │ │ ├── StringConsumer.java
│ │ │ └── StringProducer.java
│ │ │ ├── granular
│ │ │ ├── AvroConsumer.java
│ │ │ ├── AvroProducer.java
│ │ │ ├── AvroSchemaLessConsumer.java
│ │ │ ├── AvroSchemaLessProducer.java
│ │ │ ├── JsonConsumer.java
│ │ │ ├── JsonProducer.java
│ │ │ └── SchemaRegistry.java
│ │ │ └── utils
│ │ │ └── JaasUtils.java
│ │ └── proxy
│ │ ├── ProxyFromProperties.java
│ │ ├── TransparentProxyFromInstance.java
│ │ └── benchmark
│ │ ├── ConsumerBenchmark.java
│ │ └── ProducerBenchmark.java
│ └── resources
│ ├── P521.pem
│ ├── consumer.properties
│ ├── generated.reduced.json
│ ├── jaas.conf
│ ├── jaas_proxy.conf
│ ├── log4j2.xml
│ ├── producer.properties
│ ├── proxy.properties
│ └── schema-registry.properties
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── initialize_project.sh
├── proxy
├── build.gradle
├── config-example
│ ├── jaas_proxy.conf
│ └── proxy-broker.properties
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── nucypher
│ │ │ └── kafka
│ │ │ └── proxy
│ │ │ ├── Acceptor.java
│ │ │ ├── BrokerChannelBuilders.java
│ │ │ ├── ClientBrokerChannelBuilder.java
│ │ │ ├── Processor.java
│ │ │ ├── ProxyServer.java
│ │ │ ├── ProxyType.java
│ │ │ ├── Utils.java
│ │ │ ├── config
│ │ │ ├── AbstractProxyConfig.java
│ │ │ └── ProxyConfig.java
│ │ │ └── handler
│ │ │ ├── MessageHandler.java
│ │ │ ├── MessageHandlerRouter.java
│ │ │ └── MessageTransformer.java
│ └── resources
│ │ └── log4j2.xml
│ └── test
│ └── resources
│ └── jaas.conf
├── screenshots
├── EC_Keys.png
├── Full_1.png
├── Full_2.png
├── Full_3.png
├── Full_4.png
├── Full_5.png
├── Full_6.png
├── Granular_1.png
├── Granular_10.png
├── Granular_11.png
├── Granular_12.png
├── Granular_13.png
├── Granular_2.png
├── Granular_3.png
├── Granular_4.png
├── Granular_5.png
├── Granular_6.png
├── Granular_7.png
├── Granular_8.png
├── Granular_9.png
├── Kafka.png
├── Simple.png
└── ZooKeeper.png
├── settings.gradle
└── tools
├── aes.256.java.files
├── UnlimitedJCEPolicyJDK7.zip
├── UnlimitedJCEPolicyJDK8
│ └── README.txt
├── jce_policy-8.zip
└── original
│ └── security
│ ├── blacklist
│ ├── blacklisted.certs
│ ├── cacerts
│ ├── java.policy
│ ├── java.security
│ ├── javaws.policy
│ └── trusted.libraries
└── create.patch
├── apply_patch.sh
├── create_patch.sh
└── kafka-run-class.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/
2 |
3 | # Created by .ignore support plugin (hsz.mobi)
4 | ### JetBrains template
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
6 |
7 | *.iml
8 |
9 | ## Directory-based project format:
10 | .idea/
11 | # if you remove the above rule, at least ignore the following:
12 |
13 | # User-specific stuff:
14 | # .idea/workspace.xml
15 | # .idea/tasks.xml
16 | # .idea/dictionaries
17 |
18 | # Sensitive or high-churn files:
19 | # .idea/dataSources.ids
20 | # .idea/dataSources.xml
21 | # .idea/sqlDataSources.xml
22 | # .idea/dynamic.xml
23 | # .idea/uiDesigner.xml
24 |
25 | # Gradle:
26 | # .idea/gradle.xml
27 | # .idea/libraries
28 |
29 | # Mongo Explorer plugin:
30 | # .idea/mongoSettings.xml
31 |
32 | ## File-based project format:
33 | *.ipr
34 | *.iws
35 |
36 | ## Plugin-specific files:
37 |
38 | # IntelliJ
39 | /out/
40 |
41 | # mpeltonen/sbt-idea plugin
42 | .idea_modules/
43 |
44 | # JIRA plugin
45 | atlassian-ide-plugin.xml
46 |
47 | # Crashlytics plugin (for Android Studio and IntelliJ)
48 | com_crashlytics_export_strings.xml
49 | crashlytics.properties
50 | crashlytics-build.properties
51 |
52 |
53 | ### Java template
54 | *.class
55 |
56 | # Mobile Tools for Java (J2ME)
57 | .mtj.tmp/
58 |
59 | # Package Files #
60 | *.jar
61 | *.war
62 | *.ear
63 |
64 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
65 | hs_err_pid*
66 |
67 | .metadata
68 | .recommenders
69 | .project
70 | .classpath
71 | .settings
72 | target
73 |
74 | classes
75 | *.agent
76 |
77 | *main.log
78 |
79 | .vscode
80 | bower_components
81 | node_modules
82 | *.log
83 |
84 |
85 | # Created by https://www.gitignore.io/api/gradle,eclipse,maven,jetbrains
86 |
87 | ### Eclipse ###
88 |
89 | .metadata
90 | bin/
91 | tmp/
92 | *.tmp
93 | *.bak
94 | *.swp
95 | *~.nib
96 | local.properties
97 | .settings/
98 | .loadpath
99 | .recommenders
100 |
101 | # Eclipse Core
102 | .project
103 |
104 | # External tool builders
105 | .externalToolBuilders/
106 |
107 | # Locally stored "Eclipse launch configurations"
108 | *.launch
109 |
110 | # PyDev specific (Python IDE for Eclipse)
111 | *.pydevproject
112 |
113 | # CDT-specific (C/C++ Development Tooling)
114 | .cproject
115 |
116 | # JDT-specific (Eclipse Java Development Tools)
117 | .classpath
118 |
119 | # Java annotation processor (APT)
120 | .factorypath
121 |
122 | # PDT-specific (PHP Development Tools)
123 | .buildpath
124 |
125 | # sbteclipse plugin
126 | .target
127 |
128 | # Tern plugin
129 | .tern-project
130 |
131 | # TeXlipse plugin
132 | .texlipse
133 |
134 | # STS (Spring Tool Suite)
135 | .springBeans
136 |
137 | # Code Recommenders
138 | .recommenders/
139 |
140 |
141 | ### Maven ###
142 | target/
143 | pom.xml.tag
144 | pom.xml.releaseBackup
145 | pom.xml.versionsBackup
146 | pom.xml.next
147 | release.properties
148 | dependency-reduced-pom.xml
149 | buildNumber.properties
150 | .mvn/timing.properties
151 |
152 | # Exclude maven wrapper
153 | !/.mvn/wrapper/maven-wrapper.jar
154 |
155 |
156 | ### JetBrains ###
157 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
158 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
159 |
160 | # User-specific stuff:
161 | .idea/workspace.xml
162 | .idea/tasks.xml
163 |
164 | # Sensitive or high-churn files:
165 | .idea/dataSources/
166 | .idea/dataSources.ids
167 | .idea/dataSources.xml
168 | .idea/dataSources.local.xml
169 | .idea/sqlDataSources.xml
170 | .idea/dynamic.xml
171 | .idea/uiDesigner.xml
172 |
173 | # Gradle:
174 | .idea/gradle.xml
175 | .idea/libraries
176 |
177 | # Mongo Explorer plugin:
178 | .idea/mongoSettings.xml
179 |
180 | ## File-based project format:
181 | *.iws
182 |
183 | ## Plugin-specific files:
184 |
185 | # IntelliJ
186 | /out/
187 |
188 | # mpeltonen/sbt-idea plugin
189 | .idea_modules/
190 |
191 | # JIRA plugin
192 | atlassian-ide-plugin.xml
193 |
194 | # Crashlytics plugin (for Android Studio and IntelliJ)
195 | com_crashlytics_export_strings.xml
196 | crashlytics.properties
197 | crashlytics-build.properties
198 | fabric.properties
199 |
200 | ### JetBrains Patch ###
201 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
202 |
203 | # *.iml
204 | # modules.xml
205 | # .idea/misc.xml
206 | # *.ipr
207 |
208 |
209 | ### Gradle ###
210 | .gradle
211 | build/
212 | bin/
213 |
214 | # Ignore Gradle GUI config
215 | gradle-app.setting
216 |
217 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
218 | !gradle-wrapper.jar
219 |
220 | # Cache of project
221 | .gradletasknamecache
222 |
223 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
224 | # gradle/wrapper/gradle-wrapper.properties
225 |
226 | keys/*.pem
227 | out/
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "kafka"]
2 | path = kafka
3 | url = https://github.com/nucypher/kafka-oss
4 | [submodule "crypto"]
5 | path = crypto
6 | url = https://github.com/nucypher/nucypher-crypto-oss
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Run initialize_project.sh
2 |
3 | After git clone run: initialize_project.sh
4 |
5 | # Project structure
6 |
7 |
8 | ## External submodules
9 |
10 | ### nucypher-crypto-oss
11 | ### kafka-oss (0.10.1-encrypted branch)
12 |
13 |
14 | ## Java features for AES 256 bit
15 |
16 | http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#importlimits
17 | If stronger algorithms are needed (for example, AES with 256-bit keys), the JCE Unlimited Strength Jurisdiction Policy Files must be obtained and installed in the JDK/JRE.
18 |
19 | Need to download:
20 | Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE
21 |
22 | and install (overwrite) files in
23 | $JAVA_HOME/jre/lib/security
24 |
25 |
26 | ## Create patch for Kafka
27 |
28 | 1. Run
29 | ```bash
30 | tools/create.patch/create_patch.sh
31 | ```
32 | directory *patch* will be created with the following structure:
33 |
34 | ```bash
35 | bin/
36 | libs/
37 |
38 | bin/kafka-run-class.sh - added NuCypher jar's to Kafka class path
39 | libs/kafka_2.10-1.0-SNAPSHOT.jar - patched core Kafka jar
40 |
41 | libs/nucypher/ - core NuCypher jar's
42 | nucypher-kafka-admin-1.0-SNAPSHOT.jar
43 | nucypher-kafka-clients-1.0-SNAPSHOT.jar
44 | nucypher-kafka-commons-1.0-SNAPSHOT.jar
45 |
46 | libs/nucypher/lib - 3rd party jar's for NuCypher core jar's
47 | ```
48 |
49 | and a tar.gz archive nucypher-patch-kafka_2.10-1.0-SNAPSHOT.tar.gz
50 | it will contains also a script to patch Kafka
51 | tools/create.patch/apply_patch.sh
52 |
53 |
54 | 2. Run
55 |
56 | ```bash
57 | tools/create.patch/apply_patch.sh /opt/kafka
58 | ```
59 |
60 | Need to specify a path to Kafka directory, for instance: Kafka is located in /opt/kafka so
61 |
62 | ```bash
63 | tools/create.patch/apply_patch.sh /opt/kafka
64 | ```
65 |
66 |
67 |
--------------------------------------------------------------------------------
/admin/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | /build/
3 |
--------------------------------------------------------------------------------
/admin/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.nucypher.kafka'
2 |
3 | apply plugin: 'java'
4 | apply plugin: 'idea'
5 | apply plugin: 'eclipse'
6 | apply plugin: 'maven'
7 | apply plugin: 'application'
8 |
9 | sourceCompatibility = 1.8
10 | targetCompatibility = 1.8
11 |
12 | mainClassName = 'com.nucypher.kafka.admin.Console'
13 | applicationName = 'nucypher-kafka-admin'
14 |
15 | dependencies {
16 | compile project(':commons')
17 |
18 | compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion"
19 | compile "org.apache.logging.log4j:log4j-1.2-api:$log4jVersion"
20 | compile "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
21 | compile("com.101tec:zkclient:$zkclientVersion") {
22 | exclude group: "org.slf4j", module: 'slf4j-api'
23 | exclude group: "org.slf4j", module: 'slf4j-log4j12'
24 | exclude group: "log4j", module: 'log4j'
25 | }
26 |
27 | testCompile project(path: ':commons', configuration: 'testArchives')
28 | testCompile "junit:junit:$junitVersion"
29 | testCompile "org.apache.curator:curator-test:$curatorVersion"
30 | testCompile "org.apache.curator:curator-framework:$curatorVersion"
31 | testCompile "org.mockito:mockito-core:$mockitoVersion"
32 | testCompile "org.powermock:powermock-api-mockito:$powermockVersion"
33 | testCompile "org.powermock:powermock-module-junit4:$powermockVersion"
34 | testCompile "org.powermock:powermock-module-junit4-rule:$powermockVersion"
35 | }
36 |
37 | compileJava {
38 | options.encoding = 'UTF-8'
39 | }
40 |
41 | task copyToLib(type: Copy) {
42 |
43 | into "$buildDir/libs/lib"
44 | from configurations.runtime
45 |
46 | doLast {
47 | jar {
48 | archiveName = "$applicationName-${version}.${extension}"
49 |
50 | manifest {
51 | attributes("Main-Class": mainClassName)
52 | attributes("Class-Path": configurations.runtime.collect { "lib/$it.name" }.join(' '))
53 | }
54 | }
55 | }
56 |
57 | }
58 |
59 | jar.dependsOn copyToLib
60 |
61 | applicationDistribution.from("config-example/") {
62 | into "config-example"
63 | }
64 |
65 | startScripts {
66 | doLast {
67 | def windowsScriptFile = file getWindowsScript()
68 | def unixScriptFile = file getUnixScript()
69 | windowsScriptFile.text = windowsScriptFile.text.replaceAll(
70 | 'CLASSPATH=\\S*', 'CLASSPATH=%APP_HOME%\\\\lib\\\\*')
71 | unixScriptFile.text = unixScriptFile.text.replaceAll(
72 | 'CLASSPATH=\\S*\n', 'CLASSPATH=\\$APP_HOME/lib/*\n')
73 | }
74 | }
--------------------------------------------------------------------------------
/admin/config-example/commands.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "command-type": "generate",
4 | "curve-name": "secp521r1",
5 | "private-key-path": "keys/master-private-key.pem",
6 | "public-key-path": "keys/master-public-key.pem"
7 | },
8 | {
9 | "command-type": "generate",
10 | "curve-name": "secp521r1",
11 | "private-key-path": "keys/producer-private-key.pem",
12 | "public-key-path": "keys/producer-public-key.pem"
13 | },
14 | {
15 | "command-type": "generate",
16 | "curve-name": "secp521r1",
17 | "private-key-path": "keys/consumer-private-key.pem",
18 | "public-key-path": "keys/consumer-public-key.pem"
19 | },
20 | {
21 | "command-type": "add_channel",
22 | "channel-name": "full",
23 | "channel-type": "full"
24 | },
25 | {
26 | "command-type": "add_key",
27 | "master-key": "keys/master-private-key.pem",
28 | "client-key": "keys/producer-private-key.pem",
29 | "client-type": "producer",
30 | "client-name": "alice",
31 | "channel-name": "full",
32 | "expired-days": "365"
33 | },
34 | {
35 | "command-type": "add_key",
36 | "master-key": "keys/master-private-key.pem",
37 | "client-key": "keys/consumer-private-key.pem",
38 | "client-type": "consumer",
39 | "client-name": "alice",
40 | "channel-name": "full",
41 | "expired-days": "365"
42 | },
43 | {
44 | "command-type": "add_key",
45 | "client-type": "producer",
46 | "client-name": "alice",
47 | "channel-name": "full2"
48 | },
49 | {
50 | "command-type": "add_key",
51 | "master-key": "keys/master-private-key.pem",
52 | "client-key": "keys/consumer-public-key.pem",
53 | "client-type": "consumer",
54 | "client-name": "alice",
55 | "channel-name": "full2",
56 | "expired-days": "365"
57 | },
58 | {
59 | "command-type": "add_key",
60 | "client-type": "producer",
61 | "client-name": "alice",
62 | "channel-name": "full3"
63 | },
64 | {
65 | "command-type": "add_key",
66 | "client-type": "consumer",
67 | "client-name": "alice",
68 | "channel-name": "full3"
69 | },
70 | {
71 | "command-type": "add_channel",
72 | "channel-name": "granular",
73 | "channel-type": "granular",
74 | "channel-data-format": "json"
75 | },
76 | {
77 | "command-type": "add_key",
78 | "master-key": "keys/master-private-key.pem",
79 | "client-key": "keys/producer-private-key.pem",
80 | "client-type": "producer",
81 | "client-name": "alice",
82 | "channel-name": "granular",
83 | "expired-days": "365",
84 | "fields": ["a.1", "b.b", "c", "d", "e.e.e.1"]
85 | },
86 | {
87 | "command-type": "add_key",
88 | "master-key": "keys/master-private-key.pem",
89 | "client-key": "keys/consumer-private-key.pem",
90 | "client-type": "consumer",
91 | "client-name": "alice",
92 | "channel-name": "granular",
93 | "expired-days": "365",
94 | "fields": ["a.1", "b.b", "d", "e.e.e.1"]
95 | },
96 | {
97 | "command-type": "add_key",
98 | "master-key": "keys/master-private-key.pem",
99 | "client-key": "keys/master-private-key.pem",
100 | "client-type": "producer",
101 | "client-name": "alice",
102 | "channel-name": "granular2",
103 | "expired-days": "365",
104 | "fields": ["a.1", "b.b", "c", "d", "e.e.e.1"],
105 | "channel-data-format": "json"
106 | },
107 | {
108 | "command-type": "add_key",
109 | "master-key": "keys/master-private-key.pem",
110 | "client-key": "keys/consumer-public-key.pem",
111 | "client-type": "consumer",
112 | "client-name": "alice",
113 | "channel-name": "granular2",
114 | "expired-days": "365",
115 | "fields": ["a.1", "b.b", "d", "e.e.e.1"]
116 | }
117 | ]
--------------------------------------------------------------------------------
/admin/config-example/config.properties:
--------------------------------------------------------------------------------
1 | # ZooKeeper host and port
2 | zookeeper.server=localhost:2181
3 |
4 | # Scheme for admin authorization. Available values are sasl and digest.
5 | # See https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zookeeper+and+SASL
6 | admin.scheme=sasl
7 | # Admin user name
8 | admin.user=admin
9 | # Admin password for digest authorization
10 | #admin.password=admin-password
11 |
12 | # Scheme for Kafka authorization. For now available only sasl
13 | kafka.scheme=sasl
14 | # Kafka user name
15 | kafka.user=kafka
16 |
17 | # Root path for keys in ZooKeeper
18 | keys.path=/keys/admin
--------------------------------------------------------------------------------
/admin/config-example/jaas.conf:
--------------------------------------------------------------------------------
1 | Client {
2 | org.apache.zookeeper.server.auth.DigestLoginModule required
3 | username="admin"
4 | password="admin-password";
5 | };
--------------------------------------------------------------------------------
/admin/src/main/java/com/nucypher/kafka/admin/databind/CommandType.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.admin.databind;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 |
5 | /**
6 | * Command type
7 | */
8 | public enum CommandType {
9 |
10 | /**
11 | * Generate and save the re-encryption key to the storage
12 | */
13 | ADD_KEY,
14 | /**
15 | * Delete the re-encryption key from the storage
16 | */
17 | DELETE_KEY,
18 | /**
19 | * Create the channel in the storage
20 | */
21 | ADD_CHANNEL,
22 | /**
23 | * Delete the channel from the storage
24 | */
25 | DELETE_CHANNEL,
26 | /**
27 | * Generate key pair and save it to the file or files
28 | */
29 | GENERATE;
30 |
31 | @JsonCreator
32 | public static CommandType fromString(String key) {
33 | return key == null ? null :
34 | CommandType.valueOf(key.toUpperCase());
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/admin/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/admin/src/test/resources/command.json:
--------------------------------------------------------------------------------
1 | {
2 | "command-type": "add_key",
3 | "master-key": "masterKey",
4 | "client-key": "clientKey",
5 | "curve-name": "curveName",
6 | "client-type": "consumer",
7 | "client-name": "clientName",
8 | "channel-name": "channelName"
9 | }
--------------------------------------------------------------------------------
/admin/src/test/resources/commands.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "command-type": "add_key",
4 | "encryption-algorithm": "elgamal",
5 | "master-key": "masterKey",
6 | "client-key": "clientKey",
7 | "curve-name": "curveName",
8 | "client-type": "cons",
9 | "key-type": "private",
10 | "client-name": "clientName",
11 | "channel-name": "channelName",
12 | "fields": ["a.c", "b"]
13 | },
14 | {
15 | "command-type": "add_key",
16 | "master-key": "masterKey",
17 | "client-key": "clientKey",
18 | "client-type": "prod",
19 | "key-type": "pub",
20 | "client-name": "clientName",
21 | "channel-name": "channelName",
22 | "expired-date": "2017-01-01T00:00:00Z"
23 | },
24 | {
25 | "command-type": "add_key",
26 | "master-key": "masterKey",
27 | "client-key": "clientKey",
28 | "client-type": "producer",
29 | "client-name": "clientName",
30 | "channel-name": "channelName",
31 | "expired-date": "2017-01-01T00:00:00Z",
32 | "fields": ["a.c", "b"],
33 | "channel-data-accessor": "com.nucypher.kafka.clients.granular.StructuredDataAccessorStub"
34 | },
35 | {
36 | "command-type": "add_key",
37 | "master-key": "masterKey",
38 | "client-key": "clientKey",
39 | "client-type": "producer",
40 | "client-name": "clientName",
41 | "channel-name": "channelName",
42 | "expired-days": "10",
43 | "fields": ["a.c", "b"],
44 | "channel-data-format": "json"
45 | },
46 | {
47 | "command-type": "delete_key",
48 | "client-type": "producer",
49 | "client-name": "clientName",
50 | "channel-name": "channelName"
51 | },
52 | {
53 | "command-type": "delete_key",
54 | "client-type": "consumer",
55 | "client-name": "clientName",
56 | "channel-name": "channelName",
57 | "fields": ["a.c"]
58 | },
59 | {
60 | "command-type": "delete_channel",
61 | "channel-name": "channelName"
62 | },
63 | {
64 | "command-type": "add_channel",
65 | "channel-name": "channelName"
66 | },
67 | {
68 | "command-type": "add_channel",
69 | "channel-name": "channelName",
70 | "channel-type": "granular",
71 | "channel-data-accessor": "com.nucypher.kafka.clients.granular.StructuredDataAccessorStub"
72 | },
73 | {
74 | "command-type": "add_channel",
75 | "channel-name": "channelName",
76 | "channel-type": "granular",
77 | "channel-data-format": "json"
78 | },
79 | {
80 | "command-type": "add_key",
81 | "client-type": "consumer",
82 | "client-name": "clientName",
83 | "channel-name": "channelName"
84 | },
85 | {
86 | "command-type": "generate",
87 | "curve-name": "curveName",
88 | "private-key-path": "privateKey"
89 | },
90 | {
91 | "command-type": "generate",
92 | "curve-name": "curveName",
93 | "private-key-path": "privateKey",
94 | "public-key-path": "publicKey"
95 | }
96 | ]
--------------------------------------------------------------------------------
/admin/src/test/resources/jaas_test.conf:
--------------------------------------------------------------------------------
1 | Server {
2 | org.apache.zookeeper.server.auth.DigestLoginModule required
3 | user_kafka="test"
4 | user_zkAdmin1="123";
5 | };
6 |
7 | Client {
8 | org.apache.zookeeper.server.auth.DigestLoginModule required
9 | username="zkAdmin1"
10 | password="123";
11 | };
--------------------------------------------------------------------------------
/admin/src/test/resources/wrong_commands.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "command-type": "add_key",
4 | "master-key": "masterKey",
5 | "expired-date": "2017-01-01T00:00:00Z",
6 | "expired-days": "10"
7 | },
8 | {
9 | "command-type": "add_key",
10 | "master-key": "masterKey",
11 | "client-key": "clientKey",
12 | "client-type": "prod",
13 | "key-type": "pub",
14 | "client-name": "clientName",
15 | "channel-name": "channelName",
16 | "channel-data-accessor": "com.nucypher.kafka.clients.granular.StructuredDataAccessorStub",
17 | "channel-data-format": "json"
18 | },
19 | {
20 | "command-type": "delete_key",
21 | "fields": ["a.c", "b"]
22 | },
23 | {
24 | "command-type": "delete_channel"
25 | },
26 | {
27 | "command-type": "add_channel",
28 | "channel-type": "granular",
29 | "channel-data-accessor": "com.nucypher.kafka.clients.granular.StructuredDataAccessorStub",
30 | "channel-data-format": "json"
31 | },
32 | {
33 | "command-type": "generate"
34 | }
35 | ]
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | description = 'NuCypher Kafka'
2 |
3 | group 'nucypher-kafka-as-module'
4 |
5 | task wrapper(type: Wrapper) {
6 | gradleVersion = "$gradleRootVersion"
7 | }
8 |
9 | buildscript {
10 | repositories {
11 | mavenLocal()
12 | mavenCentral()
13 | maven {
14 | url "https://plugins.gradle.org/m2/"
15 | }
16 | jcenter()
17 | }
18 |
19 | dependencies {
20 | classpath "gradle.plugin.gradle-plugins:jartest:1.0"
21 | classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.4'
22 | }
23 |
24 | }
25 |
26 | allprojects {
27 | group = 'com.nucypher.kafka.encrypted'
28 |
29 | repositories {
30 | mavenLocal()
31 | mavenCentral()
32 | maven {
33 | url "http://packages.confluent.io/maven/"
34 | }
35 | }
36 |
37 | ext {
38 |
39 | rootKafkaSubPath = ':kafka'
40 |
41 | forceGroup = [
42 | // 'org.slf4j' : "$slf4jVersion",
43 | // 'ch.qos.logback' : "$logbackVersion"
44 | ]
45 |
46 | replaceArtifact = [
47 | // 'commons-logging:commons-logging': "org.slf4j:jcl-over-slf4j:$slf4jVersion",
48 | // 'log4j:log4j' : "org.slf4j:log4j-over-slf4j:$slf4jVersion",
49 | // 'org.slf4j:slf4j-log4j12' : "org.slf4j:slf4j-api:$slf4jVersion"
50 | ]
51 | }
52 |
53 | apply plugin: 'java'
54 | apply plugin: 'groovy'
55 | apply plugin: 'idea'
56 | apply plugin: 'eclipse'
57 |
58 | }
59 |
60 | subprojects { subProject ->
61 |
62 | apply from: file("${rootProject.projectDir}/dependencies_libs.gradle")
63 |
64 | dependencies {
65 | compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
66 | compile "org.bouncycastle:bcpkix-jdk15on:$bouncyCastleVersion"
67 | }
68 |
69 | compileJava.mustRunAfter clean
70 |
71 | [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'
72 | }
73 |
74 |
75 |
--------------------------------------------------------------------------------
/build_project.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # totally build project with all submodules
4 | #./gradlew clean :commons:build :admin:build :clients:build :examples:build -x test
5 | #&&
6 | # extra keys forced by kafka build
7 | ./gradlew clean build -x test -x checkstyleMain -x checkstyleTest
--------------------------------------------------------------------------------
/clients/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.nucypher.kafka'
2 |
3 | sourceCompatibility = 1.7
4 | targetCompatibility = 1.7
5 |
6 | jar {
7 | archiveName = "nucypher-kafka-clients-${version}.${extension}"
8 | }
9 |
10 | dependencies {
11 | compile project(':commons')
12 |
13 | testCompile project(path: ':commons', configuration: 'testArchives')
14 | testCompile "org.spockframework:spock-core:$spockVersion"
15 | }
16 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/MessageSerDeConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients;
2 |
3 |
4 | import com.nucypher.crypto.impl.ElGamalEncryptionAlgorithm;
5 | import com.nucypher.kafka.cipher.CipherFactory;
6 | import org.apache.kafka.common.config.AbstractConfig;
7 | import org.apache.kafka.common.config.ConfigDef;
8 |
9 | import java.util.Map;
10 |
11 | /**
12 | * Base class for configs for message serializers and deserializers
13 | */
14 | public class MessageSerDeConfig extends AbstractConfig {
15 |
16 | public static final String DEK_ENCRYPTION_ALGORITHM_CONFIG = "encryption.dek.algorithm";
17 | public static final String DEK_ENCRYPTION_ALGORITHM_DOC =
18 | "Encryption algorithm used for DEK encryption";
19 | public static final String DEK_ENCRYPTION_ALGORITHM_DEFAULT =
20 | ElGamalEncryptionAlgorithm.class.getCanonicalName();
21 |
22 | public static final String DATA_ENCRYPTION_PROVIDER_CONFIG = "encryption.data.provider";
23 | public static final String DATA_ENCRYPTION_PROVIDER_DOC = "Provider used for data encryption";
24 | public static final String DATA_ENCRYPTION_PROVIDER_DEFAULT =
25 | CipherFactory.CipherProvider.BOUNCY_CASTLE.toString();
26 |
27 | public static final String DATA_ENCRYPTION_TRANFORMATION_CONFIG = "encryption.data.transformation";
28 | public static final String DATA_ENCRYPTION_TRANFORMATION_DOC = "Transformation used for data encryption";
29 | public static final String DATA_ENCRYPTION_TRANFORMATION_DEFAULT = "AES/GCM/NoPadding";
30 |
31 | public static ConfigDef baseConfigDef() {
32 | return new ConfigDef()
33 | .define(DEK_ENCRYPTION_ALGORITHM_CONFIG,
34 | ConfigDef.Type.STRING,
35 | DEK_ENCRYPTION_ALGORITHM_DEFAULT,
36 | ConfigDef.Importance.HIGH,
37 | DEK_ENCRYPTION_ALGORITHM_DOC)
38 | .define(DATA_ENCRYPTION_PROVIDER_CONFIG,
39 | ConfigDef.Type.STRING,
40 | DATA_ENCRYPTION_PROVIDER_DEFAULT,
41 | ConfigDef.Importance.HIGH,
42 | DATA_ENCRYPTION_PROVIDER_DOC)
43 | .define(DATA_ENCRYPTION_TRANFORMATION_CONFIG,
44 | ConfigDef.Type.STRING,
45 | DATA_ENCRYPTION_TRANFORMATION_DEFAULT,
46 | ConfigDef.Importance.HIGH,
47 | DATA_ENCRYPTION_TRANFORMATION_DOC);
48 | }
49 |
50 | public MessageSerDeConfig(ConfigDef config, Map, ?> props) {
51 | super(config, props);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/StructuredMessageSerDeConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients;
2 |
3 |
4 | import org.apache.kafka.common.config.ConfigDef;
5 |
6 | /**
7 | * Base class for configs for granular serializers and deserializers
8 | */
9 | public class StructuredMessageSerDeConfig {
10 |
11 | public static final String GRANULAR_DATA_ACCESSOR_CONFIG =
12 | "encryption.granular.data.accessor";
13 | public static final String GRANULAR_DATA_ACCESSOR_DOC =
14 | "Structured data accessor for granular encryption";
15 |
16 |
17 | public static ConfigDef addGranularConfigDef(ConfigDef configDef) {
18 | return configDef
19 | .define(GRANULAR_DATA_ACCESSOR_CONFIG, ConfigDef.Type.CLASS,
20 | ConfigDef.Importance.HIGH, GRANULAR_DATA_ACCESSOR_DOC);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/decrypt/AesMessageDeserializerConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.decrypt;
2 |
3 | import com.nucypher.kafka.clients.MessageSerDeConfig;
4 | import org.apache.kafka.clients.consumer.ConsumerConfig;
5 | import org.apache.kafka.common.config.ConfigDef;
6 | import org.apache.kafka.common.serialization.ByteArrayDeserializer;
7 |
8 | import java.util.Map;
9 |
10 | /**
11 | * Configuration for {@link AesMessageDeserializer}
12 | */
13 | public class AesMessageDeserializerConfig extends MessageSerDeConfig {
14 |
15 | public static final String PRIVATE_KEY_CONFIG = "encryption.private.key";
16 | public static final String PRIVATE_KEY_DOC = "Path to the EC private key";
17 |
18 | public static final String VALUE_DESERIALIZER_CLASS_CONFIG = "encryption.value.deserializer";
19 | public static final String VALUE_DESERIALIZER_CLASS_DOC =
20 | ConsumerConfig.VALUE_DESERIALIZER_CLASS_DOC;
21 |
22 | public static final String KEY_DESERIALIZER_CLASS_CONFIG = "encryption.key.deserializer";
23 | public static final String KEY_DESERIALIZER_CLASS_DOC =
24 | ConsumerConfig.KEY_DESERIALIZER_CLASS_DOC;
25 |
26 | public static final String CACHE_DECRYPTION_CAPACITY_CONFIG = "cache.decryption.capacity";
27 | public static final String CACHE_DECRYPTION_CAPACITY_DOC = "Decryption cache capacity";
28 | public static final int CACHE_DECRYPTION_CAPACITY_DEFAULT = 200000;
29 |
30 | private static final ConfigDef CONFIG;
31 |
32 | static {
33 | CONFIG = MessageSerDeConfig.baseConfigDef()
34 | .define(PRIVATE_KEY_CONFIG,
35 | ConfigDef.Type.STRING,
36 | ConfigDef.Importance.HIGH,
37 | PRIVATE_KEY_DOC)
38 | .define(VALUE_DESERIALIZER_CLASS_CONFIG,
39 | ConfigDef.Type.CLASS,
40 | ByteArrayDeserializer.class,
41 | ConfigDef.Importance.HIGH,
42 | VALUE_DESERIALIZER_CLASS_DOC)
43 | .define(KEY_DESERIALIZER_CLASS_CONFIG,
44 | ConfigDef.Type.CLASS,
45 | ByteArrayDeserializer.class,
46 | ConfigDef.Importance.HIGH,
47 | KEY_DESERIALIZER_CLASS_DOC)
48 | .define(CACHE_DECRYPTION_CAPACITY_CONFIG,
49 | ConfigDef.Type.INT,
50 | CACHE_DECRYPTION_CAPACITY_DEFAULT,
51 | ConfigDef.Importance.LOW,
52 | CACHE_DECRYPTION_CAPACITY_DOC);
53 | }
54 |
55 | public static ConfigDef baseConfigDef() {
56 | return CONFIG;
57 | }
58 |
59 | public AesMessageDeserializerConfig(Map, ?> props) {
60 | super(CONFIG, props);
61 | }
62 |
63 | public AesMessageDeserializerConfig(ConfigDef config, Map, ?> props) {
64 | super(config, props);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/decrypt/AesStructuredMessageDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.decrypt;
2 |
3 | import com.nucypher.crypto.EncryptionAlgorithm;
4 | import com.nucypher.kafka.cipher.CipherFactory;
5 | import com.nucypher.kafka.clients.granular.DataFormat;
6 | import com.nucypher.kafka.clients.granular.StructuredDataAccessor;
7 | import com.nucypher.kafka.clients.granular.StructuredMessageHandler;
8 | import com.nucypher.kafka.errors.CommonException;
9 | import org.apache.kafka.common.config.AbstractConfig;
10 | import org.apache.kafka.common.serialization.Deserializer;
11 |
12 | import java.security.PrivateKey;
13 | import java.util.Map;
14 |
15 | /**
16 | * The structured message {@link Deserializer} which uses AES and encryption algorithm
17 | *
18 | * @param type to be deserialized into.
19 | */
20 | public class AesStructuredMessageDeserializer extends AesMessageDeserializer {
21 |
22 | private StructuredDataAccessor accessor;
23 | private StructuredMessageHandler structuredMessageHandler;
24 | private boolean isConfigured;
25 |
26 | /**
27 | * Constructor used by Kafka consumer
28 | */
29 | public AesStructuredMessageDeserializer() {
30 | super();
31 | isConfigured = false;
32 | }
33 |
34 | /**
35 | * @param deserializer Kafka deserializer
36 | * @param algorithmClass class of encryption algorithm
37 | * @param privateKey EC private key
38 | * @param format data format
39 | */
40 | public AesStructuredMessageDeserializer(
41 | Deserializer deserializer,
42 | Class extends EncryptionAlgorithm> algorithmClass,
43 | PrivateKey privateKey,
44 | DataFormat format) {
45 | this(deserializer, algorithmClass, privateKey, format.getAccessorClass());
46 | }
47 |
48 | /**
49 | * @param deserializer Kafka deserializer
50 | * @param algorithmClass class of encryption algorithm
51 | * @param privateKey EC private key
52 | * @param dataAccessorClass data accessor class
53 | */
54 | public AesStructuredMessageDeserializer(
55 | Deserializer deserializer,
56 | Class extends EncryptionAlgorithm> algorithmClass,
57 | PrivateKey privateKey,
58 | Class extends StructuredDataAccessor> dataAccessorClass) {
59 | this(deserializer, algorithmClass, privateKey, dataAccessorClass,
60 | null, null, null);
61 | }
62 |
63 | /**
64 | * @param deserializer Kafka deserializer
65 | * @param algorithmClass class of encryption algorithm
66 | * @param privateKey EC private key
67 | * @param dataAccessorClass data accessor class
68 | * @param decryptionCacheCapacity decryption cache capacity
69 | * @param provider data encryption provider
70 | * @param transformation data transformation
71 | */
72 | public AesStructuredMessageDeserializer(
73 | Deserializer deserializer,
74 | Class extends EncryptionAlgorithm> algorithmClass,
75 | PrivateKey privateKey,
76 | Class extends StructuredDataAccessor> dataAccessorClass,
77 | Integer decryptionCacheCapacity,
78 | CipherFactory.CipherProvider provider,
79 | String transformation) {
80 | super(deserializer, algorithmClass, privateKey, decryptionCacheCapacity, provider, transformation);
81 | try {
82 | accessor = dataAccessorClass.newInstance();
83 | } catch (InstantiationException | IllegalAccessException e) {
84 | throw new CommonException(e);
85 | }
86 | structuredMessageHandler = new StructuredMessageHandler(messageHandler);
87 | isConfigured = true;
88 | }
89 |
90 | @Override
91 | public void configure(Map configs, boolean isKey) {
92 | super.configure(configs, isKey);
93 | if (!isConfigured) {
94 | AbstractConfig config = new AesStructuredMessageDeserializerConfig(configs);
95 | accessor = config.getConfiguredInstance(
96 | AesStructuredMessageDeserializerConfig.GRANULAR_DATA_ACCESSOR_CONFIG,
97 | StructuredDataAccessor.class);
98 | structuredMessageHandler = new StructuredMessageHandler(messageHandler);
99 | isConfigured = true;
100 | }
101 | accessor.configure(configs, isKey);
102 | }
103 |
104 | @Override
105 | public T deserialize(String topic, byte[] data) {
106 | byte[] decrypted = structuredMessageHandler.decrypt(topic, data, accessor);
107 | return deserializer.deserialize(topic, decrypted);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/decrypt/AesStructuredMessageDeserializerConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.decrypt;
2 |
3 | import com.nucypher.kafka.clients.StructuredMessageSerDeConfig;
4 | import org.apache.kafka.common.config.ConfigDef;
5 |
6 | import java.util.Map;
7 |
8 | /**
9 | * Configuration for {@link AesStructuredMessageDeserializer}
10 | */
11 | public class AesStructuredMessageDeserializerConfig extends AesMessageDeserializerConfig {
12 |
13 | public static final String GRANULAR_DATA_ACCESSOR_CONFIG =
14 | StructuredMessageSerDeConfig.GRANULAR_DATA_ACCESSOR_CONFIG;
15 |
16 | private static final ConfigDef CONFIG;
17 |
18 | static {
19 | CONFIG = baseConfigDef();
20 | StructuredMessageSerDeConfig.addGranularConfigDef(CONFIG);
21 | }
22 |
23 | public AesStructuredMessageDeserializerConfig(Map, ?> props) {
24 | super(CONFIG, props);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/encrypt/AesMessageSerializerConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.encrypt;
2 |
3 | import com.nucypher.kafka.clients.MessageSerDeConfig;
4 | import org.apache.kafka.clients.producer.ProducerConfig;
5 | import org.apache.kafka.common.config.ConfigDef;
6 | import org.apache.kafka.common.serialization.ByteArraySerializer;
7 |
8 | import java.util.Map;
9 |
10 | /**
11 | * Configuration for {@link AesMessageSerializer}
12 | */
13 | public class AesMessageSerializerConfig extends MessageSerDeConfig {
14 |
15 | public static final String PUBLIC_KEY_CONFIG = "encryption.public.key";
16 | public static final String PUBLIC_KEY_DOC = "Path to the EC public key";
17 |
18 | public static final String VALUE_SERIALIZER_CLASS_CONFIG = "encryption.value.serializer";
19 | public static final String VALUE_SERIALIZER_CLASS_DOC =
20 | ProducerConfig.VALUE_SERIALIZER_CLASS_DOC;
21 |
22 | public static final String KEY_SERIALIZER_CLASS_CONFIG = "encryption.key.serializer";
23 | public static final String KEY_SERIALIZER_CLASS_DOC =
24 | ProducerConfig.KEY_SERIALIZER_CLASS_DOC;
25 |
26 | public static final String CACHE_ENCRYPTION_CAPACITY_CONFIG = "cache.encryption.capacity";
27 | public static final String CACHE_ENCRYPTION_CAPACITY_DOC = "Encryption cache capacity";
28 | public static final int CACHE_ENCRYPTION_CAPACITY_DEFAULT = 200000;
29 |
30 | public static final String MAX_USING_DEK_CONFIG = "encryption.dek.max.using";
31 | public static final String MAX_USING_DEK_DOC = "Max number of using each DEK";
32 | public static final int MAX_USING_DEK_DEFAULT = 1000;
33 |
34 | private static final ConfigDef CONFIG;
35 |
36 | static {
37 | CONFIG = MessageSerDeConfig.baseConfigDef()
38 | .define(PUBLIC_KEY_CONFIG,
39 | ConfigDef.Type.STRING,
40 | ConfigDef.Importance.HIGH,
41 | PUBLIC_KEY_DOC)
42 | .define(VALUE_SERIALIZER_CLASS_CONFIG,
43 | ConfigDef.Type.CLASS,
44 | ByteArraySerializer.class,
45 | ConfigDef.Importance.HIGH,
46 | VALUE_SERIALIZER_CLASS_DOC)
47 | .define(KEY_SERIALIZER_CLASS_CONFIG,
48 | ConfigDef.Type.CLASS,
49 | ByteArraySerializer.class,
50 | ConfigDef.Importance.HIGH,
51 | KEY_SERIALIZER_CLASS_DOC)
52 | .define(CACHE_ENCRYPTION_CAPACITY_CONFIG,
53 | ConfigDef.Type.INT,
54 | CACHE_ENCRYPTION_CAPACITY_DEFAULT,
55 | ConfigDef.Importance.LOW,
56 | CACHE_ENCRYPTION_CAPACITY_DOC)
57 | .define(MAX_USING_DEK_CONFIG,
58 | ConfigDef.Type.INT,
59 | MAX_USING_DEK_DEFAULT,
60 | ConfigDef.Importance.LOW,
61 | MAX_USING_DEK_DOC);
62 | }
63 |
64 | public static ConfigDef baseConfigDef() {
65 | return CONFIG;
66 | }
67 |
68 | public AesMessageSerializerConfig(Map, ?> props) {
69 | super(CONFIG, props);
70 | }
71 |
72 | public AesMessageSerializerConfig(ConfigDef config, Map, ?> props) {
73 | super(config, props);
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/clients/src/main/java/com/nucypher/kafka/clients/encrypt/AesStructuredMessageSerializerConfig.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.encrypt;
2 |
3 | import com.nucypher.kafka.clients.StructuredMessageSerDeConfig;
4 | import org.apache.kafka.common.config.ConfigDef;
5 |
6 | import java.util.Collections;
7 | import java.util.Map;
8 |
9 | /**
10 | * Configuration for {@link AesStructuredMessageSerializer}
11 | */
12 | public class AesStructuredMessageSerializerConfig extends AesMessageSerializerConfig {
13 |
14 | public static final String GRANULAR_DATA_ACCESSOR_CONFIG =
15 | StructuredMessageSerDeConfig.GRANULAR_DATA_ACCESSOR_CONFIG;
16 |
17 | public static final String FIELDS_LIST_CONFIG = "encryption.granular.fields";
18 | public static final String FIELDS_LIST_DOC = "List of fields for encryption";
19 |
20 | public static final String USE_DERIVED_KEYS_CONFIG = "encryption.granular.use.derived.keys";
21 | public static final String USE_DERIVED_KEYS_DOC = "Use derived keys for DEK encryption";
22 | public static final boolean USE_DERIVED_KEYS_DEFAULT = false;
23 |
24 | private static final ConfigDef CONFIG;
25 |
26 | static {
27 | CONFIG = baseConfigDef()
28 | .define(FIELDS_LIST_CONFIG,
29 | ConfigDef.Type.LIST,
30 | Collections.emptyList(),
31 | ConfigDef.Importance.HIGH,
32 | FIELDS_LIST_DOC)
33 | .define(USE_DERIVED_KEYS_CONFIG,
34 | ConfigDef.Type.BOOLEAN,
35 | USE_DERIVED_KEYS_DEFAULT,
36 | ConfigDef.Importance.LOW,
37 | USE_DERIVED_KEYS_DOC);
38 | StructuredMessageSerDeConfig.addGranularConfigDef(CONFIG);
39 | }
40 |
41 | public AesStructuredMessageSerializerConfig(Map, ?> props) {
42 | super(CONFIG, props);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/clients/src/main/resources/P521.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PARAMETERS-----
2 | MIIBwwIBATBNBgcqhkjOPQEBAkIB////////////////////////////////////
3 | //////////////////////////////////////////////////8wgZ8EQgH/////
4 | ////////////////////////////////////////////////////////////////
5 | /////////////////ARCAFGVPrlhjhyaH5KaIaC2hUDuotpyW5mzFfO4tImRjvEJ
6 | 4VYZOVHsfpN7FlLAvTuxvwc1c9+IPSw08e9FH9RrUD8AAxUA0J6IACkcuFOWzGcX
7 | OTKEqqDaZLoEgYUEAMaFjga3BATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFL
8 | Xnfv51ko/h3BJ6L/qN4zSLPBhWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZ
9 | mPVESVebRGgXr70XJz5mLJfucple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQ
10 | AkIB///////////////////////////////////////////6UYaHg78vlmt/zAFI
11 | 9wml0Du1ybiJnEeuu2+3HpE4ZAkCAQE=
12 | -----END EC PARAMETERS-----
13 | -----BEGIN EC PRIVATE KEY-----
14 | MIHcAgEBBEIAfzzIW+AJhTtmFSp3qqLWajKIDj1cYodPac0GYO8ku217TNUy6KmX
15 | WDmc3nY3snVf29G2cZdb8JU9aHs/+b541aagBwYFK4EEACOhgYkDgYYABAFeHVTy
16 | WkYVAACm2xj8OM5o4YkqTgzq4eSs0gT7knuoQb0dRS3LKtwfg89h7LRA3VKmRk38
17 | oHdQln0J3ZfjfM+8ogFC1j+l0bFQmbNqDSan2HaqLobrfMw/1j4Jsz5hkwCjQEoU
18 | GOlHBIJVZU0QZL3ByFXUe2ouGNXFvHpW/tPgofTR9Q==
19 | -----END EC PRIVATE KEY-----
20 | -----BEGIN PUBLIC KEY-----
21 | MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBXh1U8lpGFQAAptsY/DjOaOGJKk4M
22 | 6uHkrNIE+5J7qEG9HUUtyyrcH4PPYey0QN1SpkZN/KB3UJZ9Cd2X43zPvKIBQtY/
23 | pdGxUJmzag0mp9h2qi6G63zMP9Y+CbM+YZMAo0BKFBjpRwSCVWVNEGS9wchV1Htq
24 | LhjVxbx6Vv7T4KH00fU=
25 | -----END PUBLIC KEY-----
26 |
--------------------------------------------------------------------------------
/clients/src/main/resources/consumer.properties:
--------------------------------------------------------------------------------
1 | bootstrap.servers=localhost:9092
2 | group.id=test
3 | client.id=100500
4 | enable.auto.commit=true
5 | #key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
6 | #value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
7 |
8 | # fast session timeout makes it more fun to play with failover
9 | session.timeout.ms=10000
10 |
11 | # These buffer sizes seem to be needed to avoid consumer switching to
12 | # a mode where it processes one bufferful every 5 seconds with multiple
13 | # timeouts along the way. No idea why this happens.
14 | fetch.min.bytes=50000
15 | receive.buffer.bytes=262144
16 | max.partition.fetch.bytes=2097152
--------------------------------------------------------------------------------
/clients/src/main/resources/producer.properties:
--------------------------------------------------------------------------------
1 | bootstrap.servers=localhost:9092
2 | acks=all
3 | retries=0
4 | batch.size=16384
5 | auto.commit.interval.ms=1000
6 | linger.ms=0
7 | block.on.buffer.full=true
8 | # define it inside the code
9 | #key.serializer=org.apache.kafka.common.serialization.StringSerializer
10 | #value.serializer=org.apache.kafka.common.serialization.StringSerializer
11 |
--------------------------------------------------------------------------------
/clients/src/test/groovy/com/nucypher/kafka/clients/AesMessageEncryptorDecryptorSpec.groovy:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients
2 |
3 | import com.nucypher.crypto.EncryptionAlgorithm
4 | import com.nucypher.kafka.TestUtils
5 | import com.nucypher.kafka.clients.decrypt.AesMessageDeserializer
6 | import com.nucypher.kafka.clients.encrypt.AesMessageSerializer
7 | import com.nucypher.kafka.utils.KeyUtils
8 | import org.apache.kafka.common.serialization.StringDeserializer
9 | import org.apache.kafka.common.serialization.StringSerializer
10 | import spock.lang.Specification
11 |
12 | import java.security.KeyPair
13 |
14 | import static TestUtils.PEM
15 |
16 | /**
17 | * Test for {@link AesMessageSerializer} and {@link AesMessageDeserializer}
18 | */
19 | class AesMessageEncryptorDecryptorSpec extends Specification {
20 |
21 | static final Class extends EncryptionAlgorithm> ALGORITHM =
22 | TestUtils.ENCRYPTION_ALGORITHM_CLASS
23 |
24 | def 'encrypt and decrypt message'() {
25 | setup: 'initialization'
26 |
27 | String topic = "topic"
28 | Random random = new Random()
29 | String data = new BigInteger(130, random).toString(32)
30 |
31 | File file = new File(this.getClass().getClassLoader()
32 | .getResource(PEM).getFile())
33 | KeyPair keyPair = KeyUtils.getECKeyPairFromPEM(file.getAbsolutePath())
34 |
35 | AesMessageSerializer messageSerializer =
36 | new AesMessageSerializer<>(
37 | new StringSerializer(),
38 | ALGORITHM,
39 | keyPair.public,
40 | null
41 | )
42 |
43 | AesMessageDeserializer messageDeserializer =
44 | new AesMessageDeserializer<>(
45 | new StringDeserializer(),
46 | ALGORITHM,
47 | keyPair.private
48 | )
49 |
50 | when: 'encrypt and decrypt'
51 | byte[] encrypted = messageSerializer.serialize(topic, data)
52 | String result = messageDeserializer.deserialize(topic, encrypted)
53 |
54 | then: 'should be initial data'
55 | result == data
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/commons/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.hauner.jarTest'
2 |
3 | group 'com.nucypher.kafka'
4 |
5 | sourceCompatibility = 1.7
6 | targetCompatibility = 1.7
7 |
8 | jar {
9 | archiveName = "nucypher-kafka-commons-${version}.${extension}"
10 | }
11 |
12 | dependencies {
13 | compile project(':crypto')
14 | compile project(':kafka:clients')
15 |
16 | compile "org.slf4j:slf4j-api:$slf4jVersion"
17 | compile("org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion")
18 | compile("org.apache.logging.log4j:log4j-1.2-api:$log4jVersion")
19 |
20 | compile "org.bouncycastle:bcpkix-jdk15on:$bouncyCastleVersion"
21 |
22 | compile("com.101tec:zkclient:$zkclientVersion") {
23 | exclude group: "org.slf4j", module: 'slf4j-api'
24 | exclude group: "org.slf4j", module: 'slf4j-log4j12'
25 | exclude group: "log4j", module: 'log4j'
26 | }
27 |
28 | compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
29 | compile "com.jayway.jsonpath:json-path:$jsonPathVersion"
30 | compile "org.apache.avro:avro:$avroVersion"
31 | compile("io.confluent:kafka-avro-serializer:$confluentVersion") {
32 | exclude group: "org.slf4j", module: 'slf4j-api'
33 | exclude group: "org.slf4j", module: 'slf4j-log4j12'
34 | exclude group: "org.apache.avro", module: 'avro'
35 | exclude group: "com.fasterxml.jackson.core", module: 'jackson-databind'
36 | }
37 |
38 | compile "com.google.guava:guava:$guavaVersion"
39 | compile "org.reflections:reflections:$reflectionsVersion"
40 | compile "org.apache.commons:commons-crypto:$commonsCryptoVersion"
41 |
42 | testCompile "junit:junit:$junitVersion"
43 | testCompile "org.apache.curator:curator-test:$curatorVersion"
44 | testCompile "org.apache.curator:curator-framework:$curatorVersion"
45 | testCompile "org.spockframework:spock-core:$spockVersion"
46 | testCompile "cglib:cglib-nodep:$cglibVersion"
47 | }
48 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/Constants.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka;
2 |
3 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
4 |
5 | import java.io.Serializable;
6 |
7 | /**
8 | * Commons constants
9 | */
10 | public class Constants {
11 |
12 | public static final String BOUNCY_CASTLE_PROVIDER_NAME =
13 | new BouncyCastleProvider().getName();
14 | public static final String AES_ALGORITHM_NAME = "AES";
15 | public static final String KEY_FACTORY_ALGORITHM = "ECDSA";
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/DefaultProvider.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka;
2 |
3 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
4 |
5 | import java.security.Security;
6 |
7 | /**
8 | * Class for provider initialization
9 | */
10 | public class DefaultProvider {
11 |
12 | private static volatile boolean isInitialized = false;
13 |
14 | /**
15 | * Initialize ones per JVM
16 | */
17 | public static void initializeProvider() {
18 | if (!isInitialized) {
19 | synchronized (DefaultProvider.class) {
20 | // double check it's correct!
21 | if (!isInitialized) {
22 | Security.addProvider(new BouncyCastleProvider());
23 | isInitialized = true;
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/INamed.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka;
2 |
3 | /**
4 | * Interface for objects with names
5 | *
6 | * @author szotov
7 | */
8 | public interface INamed {
9 |
10 | /**
11 | * @return object name
12 | */
13 | public String getName();
14 |
15 | /**
16 | * @return short object name
17 | */
18 | public String getShortName();
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/cipher/CipherFactory.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.cipher;
2 |
3 | import com.nucypher.kafka.Constants;
4 | import com.nucypher.kafka.errors.CommonException;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | /**
9 | * Factory for {@link ICipher}
10 | */
11 | public class CipherFactory {
12 |
13 | private static final Logger LOGGER = LoggerFactory.getLogger(CipherFactory.class);
14 |
15 | /**
16 | * Cipher provider
17 | */
18 | public enum CipherProvider {
19 | /**
20 | * BouncyCastle provider
21 | */
22 | BOUNCY_CASTLE,
23 | /**
24 | * OpenSSL provider by JNI
25 | */
26 | OPENSSL
27 | }
28 |
29 | /**
30 | * Get {@link ICipher} instance
31 | *
32 | * @param provider {@link CipherProvider}
33 | * @param transformation transformation
34 | * @return instance of {@link ICipher}
35 | */
36 | public static ICipher getCipher(CipherProvider provider,
37 | String transformation) {
38 | LOGGER.info("Creating cipher using provider '{}' and transformation '{}'",
39 | provider, transformation);
40 | String algorithm = transformation.split("/")[0];
41 | switch (provider) {
42 | case BOUNCY_CASTLE:
43 | return new JCECipher(
44 | Constants.BOUNCY_CASTLE_PROVIDER_NAME,
45 | algorithm,
46 | transformation);
47 | case OPENSSL:
48 | return new OpenSSLCipher(algorithm, transformation);
49 | default:
50 | throw new CommonException("Unknown provider '%s'", provider);
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/cipher/ICipher.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.cipher;
2 |
3 | import java.security.Key;
4 |
5 | /**
6 | * Cipher
7 | */
8 | public interface ICipher {
9 |
10 | /**
11 | * Encrypt data using DEK and IV
12 | *
13 | * @param data data for encryption
14 | * @param key Data Encryption Key
15 | * @param IV initialization vector
16 | * @return encrypted data
17 | */
18 | public byte[] encrypt(byte[] data, Key key, byte[] IV);
19 |
20 | /**
21 | * Decrypt data using DEK and IV
22 | *
23 | * @param data data for decryption
24 | * @param key Data Encryption Key
25 | * @param IV initialization vector
26 | * @return decrypted data
27 | */
28 | public byte[] decrypt(byte[] data, Key key, byte[] IV);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/cipher/JCECipher.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.cipher;
2 |
3 | import com.nucypher.kafka.errors.CommonException;
4 |
5 | import javax.crypto.BadPaddingException;
6 | import javax.crypto.Cipher;
7 | import javax.crypto.IllegalBlockSizeException;
8 | import javax.crypto.NoSuchPaddingException;
9 | import javax.crypto.SecretKey;
10 | import javax.crypto.ShortBufferException;
11 | import javax.crypto.spec.IvParameterSpec;
12 | import javax.crypto.spec.SecretKeySpec;
13 | import java.security.InvalidAlgorithmParameterException;
14 | import java.security.InvalidKeyException;
15 | import java.security.Key;
16 | import java.security.NoSuchAlgorithmException;
17 | import java.security.NoSuchProviderException;
18 | import java.security.spec.AlgorithmParameterSpec;
19 | import java.util.Arrays;
20 |
21 | /**
22 | * JCE cipher
23 | */
24 | public class JCECipher implements ICipher {
25 |
26 | private String transformation;
27 | private String algorithm;
28 | private String provider;
29 |
30 | /**
31 | * @param provider provider
32 | * @param algorithm algorithm
33 | * @param transformation transformation string
34 | */
35 | public JCECipher(String provider, String algorithm, String transformation) {
36 | this.provider = provider;
37 | this.algorithm = algorithm;
38 | this.transformation = transformation;
39 | }
40 |
41 | private Cipher getCipher(boolean isEncryption, Key key, byte[] IV) {
42 | Cipher cipher;
43 | try {
44 | cipher = Cipher.getInstance(transformation, provider);
45 | } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException ex) {
46 | throw new CommonException(
47 | ex,
48 | "Unable to get instance of Cipher for %s for security provider: %s",
49 | transformation, provider);
50 | }
51 |
52 | SecretKey keyValue = new SecretKeySpec(key.getEncoded(), algorithm);
53 | AlgorithmParameterSpec IVspec = new IvParameterSpec(IV);
54 |
55 | try {
56 | cipher.init(isEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
57 | keyValue, IVspec);
58 | } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
59 | throw new CommonException("Unable to initialize Cipher", ex);
60 | }
61 | return cipher;
62 | }
63 |
64 | @Override
65 | public byte[] encrypt(byte[] data, Key key, byte[] IV) {
66 | return translate(true, data, key, IV);
67 | }
68 |
69 | @Override
70 | public byte[] decrypt(byte[] data, Key key, byte[] IV) {
71 | return translate(false, data, key, IV);
72 | }
73 |
74 | private byte[] translate(boolean isEncryption, byte[] data, Key key, byte[] IV) {
75 | Cipher cipher = getCipher(isEncryption, key, IV);
76 | byte[] output = new byte[cipher.getOutputSize(data.length)];
77 | try {
78 | int updateBytes = cipher.update(data, 0, data.length, output, 0);
79 | int finalBytes = cipher.doFinal(data, 0, 0, output, updateBytes);
80 | if (updateBytes + finalBytes < output.length) {
81 | output = Arrays.copyOf(output, updateBytes + finalBytes);
82 | }
83 | } catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException e) {
84 | throw new CommonException(e);
85 | }
86 | return output;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/cipher/OpenSSLCipher.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.cipher;
2 |
3 | import com.nucypher.kafka.errors.CommonException;
4 | import org.apache.commons.crypto.cipher.CryptoCipher;
5 | import org.apache.commons.crypto.cipher.CryptoCipherFactory;
6 | import org.apache.commons.crypto.utils.Utils;
7 |
8 | import javax.crypto.BadPaddingException;
9 | import javax.crypto.Cipher;
10 | import javax.crypto.IllegalBlockSizeException;
11 | import javax.crypto.SecretKey;
12 | import javax.crypto.ShortBufferException;
13 | import javax.crypto.spec.IvParameterSpec;
14 | import javax.crypto.spec.SecretKeySpec;
15 | import java.io.IOException;
16 | import java.security.InvalidAlgorithmParameterException;
17 | import java.security.InvalidKeyException;
18 | import java.security.Key;
19 | import java.security.spec.AlgorithmParameterSpec;
20 | import java.util.Arrays;
21 | import java.util.Properties;
22 |
23 | /**
24 | * OpenSSL cipher
25 | */
26 | public class OpenSSLCipher implements ICipher {
27 |
28 | static {
29 | try {
30 | System.loadLibrary("crypto");
31 | } catch (UnsatisfiedLinkError e) {
32 | System.loadLibrary("libcrypto");
33 | }
34 | }
35 |
36 | private String transformation;
37 | private String algorithm;
38 |
39 | /**
40 | * @param algorithm algorithm
41 | * @param transformation transformation string
42 | */
43 | public OpenSSLCipher(String algorithm, String transformation) {
44 | this.algorithm = algorithm;
45 | this.transformation = transformation;
46 | }
47 |
48 | private CryptoCipher getCipher(boolean isEncryption, Key key, byte[] IV) {
49 | Properties properties = new Properties();
50 | properties.setProperty(CryptoCipherFactory.CLASSES_KEY,
51 | CryptoCipherFactory.CipherProvider.OPENSSL.getClassName());
52 | CryptoCipher cipher;
53 | try {
54 | cipher = Utils.getCipherInstance(transformation, properties);
55 | } catch (IOException ex) {
56 | throw new CommonException(ex);
57 | }
58 |
59 | SecretKey keyValue = new SecretKeySpec(key.getEncoded(), algorithm);
60 | AlgorithmParameterSpec IVspec = new IvParameterSpec(IV);
61 |
62 | try {
63 | cipher.init(isEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
64 | keyValue, IVspec);
65 | } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
66 | throw new CommonException("Unable to initialize Cipher", ex);
67 | }
68 | return cipher;
69 | }
70 |
71 | /**
72 | * Encrypt data using DEK and IV
73 | *
74 | * @param data data for encryption
75 | * @param key Data Encryption Key
76 | * @param IV initialization vector
77 | * @return encrypted data
78 | */
79 | public byte[] encrypt(byte[] data, Key key, byte[] IV) {
80 | return translate(true, data, key, IV);
81 | }
82 |
83 | /**
84 | * Decrypt data using DEK and IV
85 | *
86 | * @param data data for decryption
87 | * @param key Data Encryption Key
88 | * @param IV initialization vector
89 | * @return decrypted data
90 | */
91 | public byte[] decrypt(byte[] data, Key key, byte[] IV) {
92 | return translate(false, data, key, IV);
93 | }
94 |
95 | private byte[] translate(boolean isEncryption, byte[] data, Key key, byte[] IV) {
96 | byte[] output = new byte[2 * data.length];
97 | try (CryptoCipher cipher = getCipher(isEncryption, key, IV)) {
98 | int updateBytes = cipher.update(data, 0, data.length, output, 0);
99 | int finalBytes = cipher.doFinal(data, 0, 0, output, updateBytes);
100 | return Arrays.copyOf(output, updateBytes + finalBytes);
101 | } catch (IOException |
102 | ShortBufferException |
103 | BadPaddingException |
104 | IllegalBlockSizeException ex) {
105 | throw new CommonException("Unable to cipher", ex);
106 | }
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/clients/EncryptedDataEncryptionKey.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients;
2 |
3 | import com.google.common.primitives.Ints;
4 |
5 | import java.util.Arrays;
6 | import java.util.Objects;
7 |
8 | /**
9 | * Encrypted Data Encryption Key (EDEK)
10 | */
11 | public class EncryptedDataEncryptionKey {
12 |
13 | private static final byte IS_COMPLEX_TRUE = 1;
14 | private static final byte IS_COMPLEX_FALSE = 0;
15 |
16 | private byte[] bytes;
17 | private boolean isComplex;
18 |
19 | /**
20 | * @param bytes EDEK bytes
21 | * @param isComplex is EDEK complex
22 | */
23 | public EncryptedDataEncryptionKey(byte[] bytes, boolean isComplex) {
24 | this.bytes = bytes;
25 | this.isComplex = isComplex;
26 | }
27 |
28 | /**
29 | * @param bytes EDEK bytes
30 | */
31 | public EncryptedDataEncryptionKey(byte[] bytes) {
32 | this(bytes, false);
33 | }
34 |
35 | /**
36 | * @return EDEK bytes
37 | */
38 | public byte[] getBytes() {
39 | return bytes;
40 | }
41 |
42 |
43 | /**
44 | * @return is EDEK complex
45 | */
46 | public boolean isComplex() {
47 | return isComplex;
48 | }
49 |
50 | /**
51 | * @return size of serialized EDEK
52 | */
53 | public int size() {
54 | return bytes.length + 5;
55 | }
56 |
57 | /**
58 | * Serialize {@link EncryptedDataEncryptionKey}
59 | *
60 | * @return serialized data
61 | */
62 | public byte[] serialize() {
63 | return serialize(new byte[size()], 0);
64 | }
65 |
66 | /**
67 | * Serialize {@link EncryptedDataEncryptionKey} into buffer
68 | *
69 | * @param buffer buffer byte array
70 | * @param offset start offset
71 | * @return serialized data
72 | */
73 | public byte[] serialize(byte[] buffer, int offset) {
74 | buffer[offset] = !isComplex ? IS_COMPLEX_FALSE : IS_COMPLEX_TRUE;
75 | byte[] edekLengthBytes = Ints.toByteArray(bytes.length);
76 | System.arraycopy(
77 | edekLengthBytes, 0, buffer, offset + 1, edekLengthBytes.length);
78 | System.arraycopy(
79 | bytes, 0, buffer, offset + edekLengthBytes.length + 1, bytes.length);
80 | return buffer;
81 | }
82 |
83 | /**
84 | * Deserialize {@link EncryptedDataEncryptionKey} from byte array
85 | *
86 | * @param bytes byte array
87 | * @return {@link EncryptedDataEncryptionKey} instance
88 | */
89 | public static EncryptedDataEncryptionKey deserialize(byte[] bytes) {
90 | return deserialize(bytes, 0);
91 | }
92 |
93 | /**
94 | * Deserialize {@link EncryptedDataEncryptionKey} from byte array
95 | *
96 | * @param bytes byte array
97 | * @param offset start offset
98 | * @return {@link EncryptedDataEncryptionKey} instance
99 | */
100 | public static EncryptedDataEncryptionKey deserialize(byte[] bytes, int offset) {
101 | byte isComplexByte = bytes[offset];
102 | byte[] edek = new byte[Ints.fromBytes(
103 | bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4])];
104 | System.arraycopy(bytes, offset + 5, edek, 0, edek.length);
105 | return new EncryptedDataEncryptionKey(edek, isComplexByte == IS_COMPLEX_TRUE);
106 | }
107 |
108 | @Override
109 | public boolean equals(Object o) {
110 | if (this == o) return true;
111 | if (o == null || getClass() != o.getClass()) return false;
112 | EncryptedDataEncryptionKey that = (EncryptedDataEncryptionKey) o;
113 | return isComplex == that.isComplex &&
114 | Arrays.equals(bytes, that.bytes);
115 | }
116 |
117 | @Override
118 | public int hashCode() {
119 | return Objects.hash(bytes, isComplex);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/clients/Message.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients;
2 |
3 | import com.google.common.primitives.Ints;
4 |
5 | import java.util.Arrays;
6 | import java.util.Objects;
7 |
8 | /**
9 | * Message
10 | */
11 | public class Message {
12 |
13 | private byte[] payload;
14 | private byte[] iv;
15 | private EncryptedDataEncryptionKey edek;
16 |
17 | /**
18 | * @param payload data
19 | * @param iv IV bytes
20 | * @param edek EDEK
21 | */
22 | public Message(byte[] payload, byte[] iv, EncryptedDataEncryptionKey edek) {
23 | this.payload = payload;
24 | this.edek = edek;
25 | this.iv = iv;
26 | }
27 |
28 | /**
29 | * @param payload data
30 | * @param iv IV bytes
31 | * @param edek EDEK bytes
32 | */
33 | public Message(byte[] payload, byte[] iv, byte[] edek) {
34 | this(payload, iv, new EncryptedDataEncryptionKey(edek));
35 | }
36 |
37 | /**
38 | * @param payload data
39 | * @param iv IV bytes
40 | * @param edek EDEK bytes
41 | * @param isComplex is EDEK complex
42 | */
43 | public Message(byte[] payload, byte[] iv, byte[] edek, boolean isComplex) {
44 | this(payload, iv, new EncryptedDataEncryptionKey(edek, isComplex));
45 | }
46 |
47 | /**
48 | * @param payload data
49 | * @param iv IV bytes
50 | */
51 | public Message(byte[] payload, byte[] iv) {
52 | this(payload, iv, (EncryptedDataEncryptionKey) null);
53 | }
54 |
55 | /**
56 | * @return data
57 | */
58 | public byte[] getPayload() {
59 | return payload;
60 | }
61 |
62 | /**
63 | * @return EDEK
64 | */
65 | public EncryptedDataEncryptionKey getEDEK() {
66 | return edek;
67 | }
68 |
69 | /**
70 | * @param edek EDEK
71 | */
72 | public void setEDEK(EncryptedDataEncryptionKey edek) {
73 | this.edek = edek;
74 | }
75 |
76 | /**
77 | * @return IV
78 | */
79 | public byte[] getIV() {
80 | return iv;
81 | }
82 |
83 | /**
84 | * Serialize {@link Message}
85 | *
86 | * @return serialized data
87 | */
88 | public byte[] serialize() {
89 | int length = 8 + payload.length + iv.length + (edek != null ? edek.size() : 0);
90 | byte[] data = new byte[length];
91 | byte[] payloadLengthBytes = Ints.toByteArray(payload.length);
92 | byte[] ivLengthBytes = Ints.toByteArray(iv.length);
93 | System.arraycopy(payloadLengthBytes, 0, data, 0, payloadLengthBytes.length);
94 | System.arraycopy(payload, 0, data, 4, payload.length);
95 | System.arraycopy(
96 | ivLengthBytes, 0, data, 4 + payload.length, ivLengthBytes.length);
97 | System.arraycopy(iv, 0, data, 8 + payload.length, iv.length);
98 | if (edek != null) {
99 | data = edek.serialize(data, 8 + payload.length + iv.length);
100 | }
101 | return data;
102 | }
103 |
104 | /**
105 | * Deserialize {@link Message} from byte array
106 | *
107 | * @param bytes byte array
108 | * @return {@link Message} instance
109 | */
110 | public static Message deserialize(byte[] bytes) {
111 | byte[] payload = new byte[
112 | Ints.fromBytes(bytes[0], bytes[1], bytes[2], bytes[3])];
113 | System.arraycopy(bytes, 4, payload, 0, payload.length);
114 | int i = 4 + payload.length;
115 | byte[] iv = new byte[
116 | Ints.fromBytes(bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3])];
117 | System.arraycopy(bytes, i + 4, iv, 0, iv.length);
118 | EncryptedDataEncryptionKey edek = null;
119 | if (8 + payload.length + iv.length < bytes.length) {
120 | edek = EncryptedDataEncryptionKey.deserialize(
121 | bytes, 8 + payload.length + iv.length);
122 | }
123 |
124 | return new Message(payload, iv, edek);
125 | }
126 |
127 | @Override
128 | public boolean equals(Object o) {
129 | if (this == o) return true;
130 | if (o == null || getClass() != o.getClass()) return false;
131 | Message message = (Message) o;
132 | return Arrays.equals(payload, message.payload) &&
133 | Arrays.equals(iv, message.iv) &&
134 | Objects.equals(edek, message.edek);
135 | }
136 |
137 | @Override
138 | public int hashCode() {
139 | return Objects.hash(payload, iv, edek);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/clients/granular/DataFormat.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.granular;
2 |
3 | import com.nucypher.kafka.INamed;
4 | import com.nucypher.kafka.errors.CommonException;
5 |
6 | /**
7 | * Data format enum
8 | *
9 | * @author szotov
10 | */
11 | public enum DataFormat implements INamed {
12 |
13 | /**
14 | * Avro
15 | */
16 | AVRO("avro", AvroDataAccessor.class),
17 | /**
18 | * Avro Schema Less
19 | */
20 | AVRO_SCHEMA_LESS("avro_schema_less", AvroSchemaLessDataAccessor.class),
21 | /**
22 | * JSON
23 | */
24 | JSON("json", JsonDataAccessor.class);
25 |
26 | private String shortName;
27 | private Class extends StructuredDataAccessor> accessorClass;
28 |
29 | DataFormat(String shortName, Class extends StructuredDataAccessor> accessorClass) {
30 | this.shortName = shortName;
31 | this.accessorClass = accessorClass;
32 | }
33 |
34 | @Override
35 | public String getShortName() {
36 | return shortName;
37 | }
38 |
39 | @Override
40 | public String getName() {
41 | return name();
42 | }
43 |
44 | /**
45 | * @return accessor class
46 | */
47 | public Class extends StructuredDataAccessor> getAccessorClass() {
48 | return accessorClass;
49 | }
50 |
51 | /**
52 | * Check whether one of the formats contains the accessor class
53 | *
54 | * @param accessorClass accessor class to check
55 | * @return result of checking
56 | */
57 | public static boolean contains(Class extends StructuredDataAccessor> accessorClass) {
58 | for (DataFormat format : values()) {
59 | if (format.getAccessorClass().equals(accessorClass)) {
60 | return true;
61 | }
62 | }
63 | return false;
64 | }
65 |
66 | /**
67 | * Get data format from accessor class
68 | *
69 | * @param accessorClass accessor class
70 | * @return data format
71 | */
72 | public static DataFormat valueOf(Class extends StructuredDataAccessor> accessorClass) {
73 | for (DataFormat format : DataFormat.values()) {
74 | if (format.getAccessorClass().equals(accessorClass)) {
75 | return format;
76 | }
77 | }
78 | throw new CommonException(
79 | "No such data format with accessor class '%s'", accessorClass.getName());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/clients/granular/OneMessageDataAccessor.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.granular;
2 |
3 | import java.util.NoSuchElementException;
4 |
5 | /**
6 | * Abstract {@link StructuredDataAccessor} with only one message
7 | */
8 | public abstract class OneMessageDataAccessor implements StructuredDataAccessor {
9 |
10 | private boolean hasNext = true;
11 | private boolean isEmpty = false;
12 |
13 | /**
14 | * @param isEmpty is message empty
15 | */
16 | protected void setEmpty(boolean isEmpty) {
17 | hasNext = !isEmpty;
18 | this.isEmpty = isEmpty;
19 | }
20 |
21 | @Override
22 | public boolean hasNext() {
23 | return hasNext;
24 | }
25 |
26 | @Override
27 | public void seekToNext() throws NoSuchElementException {
28 | if (!hasNext()) {
29 | throw new NoSuchElementException();
30 | }
31 | hasNext = false;
32 | }
33 |
34 | @Override
35 | public void reset() {
36 | hasNext = !isEmpty;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/clients/granular/StructuredDataAccessor.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.clients.granular;
2 |
3 | import java.util.Map;
4 | import java.util.NoSuchElementException;
5 | import java.util.Set;
6 |
7 | /**
8 | * The structured data accessor. The implementation must have default constructor
9 | */
10 | public interface StructuredDataAccessor {
11 |
12 | /**
13 | * Configure this class.
14 | *
15 | * @param configs configs in key/value pairs
16 | * @param isKey whether is for key or value
17 | */
18 | public void configure(Map configs, boolean isKey);
19 |
20 | /**
21 | * Deserialize data into internal object
22 | *
23 | * @param topic topic associated with data
24 | * @param data input data
25 | */
26 | public void deserialize(String topic, byte[] data);
27 |
28 | /**
29 | * Serialize internal data into byte array
30 | *
31 | * @return serialized data
32 | */
33 | public byte[] serialize();
34 |
35 | /**
36 | * @return all fields which available for encryption
37 | */
38 | public Set getAllFields();
39 |
40 | /**
41 | * @return all encrypted fields and their EDEK
42 | */
43 | public Map getAllEDEKs();
44 |
45 | /**
46 | * Get encrypted fields data
47 | *
48 | * @param field input field
49 | * @return field data
50 | */
51 | public byte[] getEncrypted(String field);
52 |
53 | /**
54 | * Get unencrypted fields data
55 | *
56 | * @param field input field
57 | * @return field data
58 | */
59 | public byte[] getUnencrypted(String field);
60 |
61 | /**
62 | * Add encrypted field
63 | *
64 | * @param field field name
65 | * @param data encrypted data
66 | */
67 | public void addEncrypted(String field, byte[] data);
68 |
69 | /**
70 | * Add EDEK
71 | *
72 | * @param field field name
73 | * @param edek EDEK
74 | */
75 | public void addEDEK(String field, byte[] edek);
76 |
77 | /**
78 | * Add unencrypted field
79 | *
80 | * @param field field name
81 | * @param data non-encrypted data
82 | */
83 | public void addUnencrypted(String field, byte[] data);
84 |
85 | /**
86 | * Remove EDEK
87 | *
88 | * @param field field name
89 | */
90 | public void removeEDEK(String field);
91 |
92 | /**
93 | * @return {@code true} if the iteration has more elements
94 | */
95 | public boolean hasNext();
96 |
97 | /**
98 | * Seek to the next element in the iteration
99 | *
100 | * @throws NoSuchElementException if the iteration has no more elements
101 | */
102 | public void seekToNext() throws NoSuchElementException;
103 |
104 | /**
105 | * Reset to the first message
106 | */
107 | public void reset();
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/errors/CommonException.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.errors;
2 |
3 | /**
4 | * {@link RuntimeException} wrapper
5 | */
6 | public class CommonException extends RuntimeException {
7 |
8 | private static final long serialVersionUID = 487684204074816608L;
9 |
10 | /**
11 | * @param cause the cause
12 | * @param format see format in {@link String#format(String, Object...)}
13 | * @param args see args in {@link String#format(String, Object...)}
14 | */
15 | public CommonException(Throwable cause, String format, Object... args) {
16 | super(String.format(format, args), cause);
17 | }
18 |
19 | /**
20 | * @param format see format in {@link String#format(String, Object...)}
21 | * @param args see args in {@link String#format(String, Object...)}
22 | */
23 | public CommonException(String format, Object... args) {
24 | super(String.format(format, args));
25 | }
26 |
27 | /**
28 | * See {@link RuntimeException#RuntimeException()}
29 | */
30 | public CommonException() {
31 | super();
32 | }
33 |
34 | /**
35 | * See {@link RuntimeException#RuntimeException(String, Throwable, boolean, boolean)}
36 | */
37 | protected CommonException(String message, Throwable cause,
38 | boolean enableSuppression, boolean writableStackTrace) {
39 | super(message, cause, enableSuppression, writableStackTrace);
40 | }
41 |
42 | /**
43 | * See {@link RuntimeException#RuntimeException(String, Throwable)}
44 | */
45 | public CommonException(String message, Throwable cause) {
46 | super(message, cause);
47 | }
48 |
49 | /**
50 | * See {@link RuntimeException#RuntimeException(String)}
51 | */
52 | public CommonException(String message) {
53 | super(message);
54 | }
55 |
56 | /**
57 | * See {@link RuntimeException#RuntimeException(Throwable)}
58 | */
59 | public CommonException(Throwable cause) {
60 | super(cause);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/utils/AESKeyGenerators.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.utils;
2 |
3 | import com.nucypher.kafka.DefaultProvider;
4 | import com.nucypher.kafka.errors.CommonException;
5 |
6 | import javax.crypto.KeyGenerator;
7 | import javax.crypto.spec.SecretKeySpec;
8 | import java.security.Key;
9 | import java.security.NoSuchAlgorithmException;
10 | import java.security.NoSuchProviderException;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | import static com.nucypher.kafka.Constants.BOUNCY_CASTLE_PROVIDER_NAME;
15 | import static com.nucypher.kafka.Constants.AES_ALGORITHM_NAME;
16 |
17 | /**
18 | * AES key generators
19 | */
20 | public class AESKeyGenerators {
21 |
22 | private static final int BITS_IN_BYTE = 8;
23 | private static final Map keyGenerators = new HashMap<>();
24 |
25 | static {
26 | DefaultProvider.initializeProvider();
27 | getKeyGenerator(128);
28 | getKeyGenerator(192);
29 | getKeyGenerator(256);
30 | }
31 |
32 | private static KeyGenerator getKeyGenerator(int size) {
33 | KeyGenerator keyGenerator = keyGenerators.get(size);
34 | if (keyGenerator == null) {
35 | synchronized (AESKeyGenerators.class) {
36 | keyGenerator = keyGenerators.get(size);
37 | if (keyGenerator == null) {
38 | try {
39 | keyGenerator = KeyGenerator.getInstance(
40 | AES_ALGORITHM_NAME, BOUNCY_CASTLE_PROVIDER_NAME);
41 | } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
42 | throw new CommonException(e);
43 | }
44 | keyGenerator.init(size);
45 | keyGenerators.put(size, keyGenerator);
46 | }
47 | }
48 | }
49 | return keyGenerator;
50 | }
51 |
52 | /**
53 | * Generate DEK - AES
54 | *
55 | * @param size key size in bytes
56 | * @return DEK
57 | */
58 | public static Key generateDEK(int size) {
59 | size = BITS_IN_BYTE * size;
60 | KeyGenerator keyGenerator = getKeyGenerator(size);
61 | return keyGenerator.generateKey();
62 | }
63 |
64 | /**
65 | * Create Key from byte array
66 | *
67 | * @param keyBytes - byte[]
68 | * @param symmetricAlgorithmName - "AES"
69 | * @return Key
70 | */
71 | public static Key create(byte[] keyBytes, String symmetricAlgorithmName) {
72 | return new SecretKeySpec(keyBytes, 0, keyBytes.length, symmetricAlgorithmName);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/nucypher/kafka/utils/AvroUtils.java:
--------------------------------------------------------------------------------
1 | package com.nucypher.kafka.utils;
2 |
3 | import com.nucypher.kafka.errors.CommonException;
4 | import org.apache.avro.Schema;
5 | import org.apache.avro.generic.GenericDatumReader;
6 | import org.apache.avro.generic.GenericDatumWriter;
7 | import org.apache.avro.io.BinaryDecoder;
8 | import org.apache.avro.io.BinaryEncoder;
9 | import org.apache.avro.io.DatumReader;
10 | import org.apache.avro.io.DatumWriter;
11 | import org.apache.avro.io.DecoderFactory;
12 | import org.apache.avro.io.EncoderFactory;
13 | import org.apache.avro.specific.SpecificDatumReader;
14 |
15 | import java.io.ByteArrayOutputStream;
16 | import java.io.IOException;
17 |
18 | /**
19 | * Class for working with Avro format
20 | *
21 | * @author szotov
22 | */
23 | public class AvroUtils {
24 |
25 | /**
26 | * Get object from bytes
27 | *
28 | * @param schema schema
29 | * @param data byte array
30 | * @return deserialized object
31 | */
32 | public static Object deserialize(Schema schema, byte[] data) {
33 | return deserialize(schema, data, false);
34 | }
35 |
36 |
37 | /**
38 | * Get object from bytes
39 | *
40 | * @param schema schema
41 | * @param data byte array
42 | * @param useSpecificAvroReader use specific reader
43 | * @return deserialized object
44 | */
45 | public static Object deserialize(Schema schema, byte[] data, boolean useSpecificAvroReader) {
46 | BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(data, null);
47 | DatumReader