Не Let’s Encrypted единым — как сделать сертификат на локальном удостоверяющем центре

Дисклеймер

В этой статье выражено личное мнение автора, его видение мира, его путь, и это все не претендует на абсолютную верность и объективность. Автор не несет никакой ответственности за последствия использования данной информации, он только надеется что эта информация поможет сделать кому-то жизнь проще.

Зачем нам все это надо?

Война… Война никогда не меняется… (с) Fallout
Да, война за свои данные никогда не меняется и не останавливается. Она идет каждую минуту, каждую секунду, каждый тик процессора.
Но к черту эту пафосную фигню, оставим это маркетологам по продаже продуктов по безопасности. Сегодняшняя статья маленькая, но надеюсь весьма полезная — как сделать себе сертификат подписанный своим собственным центром сертификации.

Чего НЕ будет в статье

  • как настроить свой собственный центр сертификации
  • авторизация по сертификатом
  • внедрение x509
  • какой-то экзотики связной с сертификатами

Что будет в статье

  • как создать сертификат подписанный локальным сервером сертификации
  • как создать сертификат даже при условии что система не умеет генерировать запрос

Кому интересно — прошу под кат

Идея этой статьи возникла уже давно, еще в те времена когда мы только начинали думать над своим собственным PKI. Мануалов как поставить свой центр сертификации было много, даже попадались весьма толковые, с объяснением что и как надо сделать. Но как правило, после установки центра сертификации (ЦС) начинается как раз самое веселое, которое почему-то обычно вообще не описывается. Ну или описывается отрывочно, в комментариях на форумах — ибо для тех кто понимает — это очевидно, а те кто не понимает… Тому приходиться туго =)

При генерации сертификата бывают два варианта простой и посложнее. Простой — система сама создала запроса, второй (посложнее) — система просто хочет от вас получить ключи. Так же есть веселый вариант когда вам нужно сделать сертификат для Linux-машин. Его тоже опишу.

Вариант 1. Простой

Приложение само умеет генерировать запрос на сертификат.
В этом случае мы получаем файл с расширением .csr или .req
Примечание: После создания в оснастке Exchange файла запроса .req, его нужно пересохранить в кодировке ANSI.

Чтобы получить заверенный сертификат нужно сделать следующее:

  1. Заходим на выдающий сервер (в зависимости от вашей конфигурации это может быть и корневой сервер сертификации)
  2. Запускаем из-под админа cmd
  3. Вводим CERTREQ -attrib «CertificateTemplate:WebServer» C:\%сert_patch%\%cert_name%.csr
    attrib - это имя шаблона, по которому делается сертификат. Он определяет, что именно можно удостоверять этим сертификатом. Стандартно это проверка подлинности сервера, бывают еще проверка подлинности клиента, подпись кода, почта, еще много всякого разного C:\%сert_patch%\%cert_name%.csr - это путь к файлу запроса. Может быть csr (я так понимаю в основном *nix-системы) или req (я так понимаю windows)
  4. Вылезет окошко с выбором удостоверяющего центра:
    image
  5. Если нам повезло и мы нигде не накосячили у нас появится окно с предложением сохранить файл с расширением .cer – это как раз то, что нам нужно – подписанная открытая часть.
  6. Полученный файлик мы скармливаем запросившему приложению и получаем правильный феншуйный сертификат у приложения.

Вариант 2. Сложный, но более гибкий.

Бывают случаи, когда приложение не умеет генерировать запросы и хочет получить в использование уже все готовое (открытый и закрытый ключ вместе)
С одной стороны это хуже, так как больше действий. С другой – лучше, мы можем забить больше нужной нам информации и правильных имен. Например, мы можем сделать красивый сертификат, который будет валидный при обращении как к короткому имений https://myserver так и по fqdn-имени https://myserver.company.local так и даже по ip https://192.168.0.3
Ну в общем так случилось, что приложение ничего не умеет, а сертификат ей все-таки нужен. В этом случае будем действовать так:

  1. Создаем файл %name%.inf. В него вписываем:

    [Version] -- Версия она и в Африке версия Signature="$Windows NT$" -- не знаю что это, возможно подскажут в комментариях [NewRequest] – надеюсь понятно без слов Subject = "CN=hardware.company.local";  --каноническое имя на которое выдается сертификат. В большинстве случаев оно и единственное. Если обращаться к сервису/железке не по этому имени (к примеру просто hardware), то сертификат будет считаться не действительным. Обходится прописыванием дополнительных имен (об этом ниже) Exportable = TRUE; -- обозначает можно ли выгружать приватный ключ. Если поставить false то этот сертификат можно будет использовать только на этом сервере. Никуда не унесешь KeyLength = 2048; -- длина ключа KeySpec = 1; -- не знаю что это, возможно подскажут в комментариях KeyUsage = 0xA0; -- не знаю что это, возможно подскажут в комментариях MachineKeySet = TRUE -- не знаю что это, возможно подскажут в комментариях ProviderName = "Microsoft RSA SChannel Cryptographic Provider" -- это провайдер шифрования. Имеет смысл менять ммм…. Никогда. Ну или точно знаешь зачем это нужно. RequestType = PKCS10; -- тип запроса. менять нужно если точно уверен что ты делаешь [EnhancedKeyUsageExtension] – блок описания для чего может использоваться этот сертификат OID=1.3.6.1.5.5.7.3.1 ; Server Authentication  -- проверка подлинности сервера OID=1.3.6.1.5.5.7.3.2 ; Client Authentication – проверка подлинности клиента [RequestAttributes] – ну и складное на закуску, дополнительные имена в сертификат SAN = "dns=hardware.company.local&" -- полное имя DNS _continue_ = "dns=hardware&" -- сокращенное имя _continue_ = "ipaddress=192.168.0.1" -- ip-адрес если нужно добавить несколько ip-адресов, или dns-имен то добавляем соответствующую строчку. Одна строчка – одно имя или ip-адрес. В конце имени или ip адреса должен стоять знак &. В последней строчке знака & ставить не надо - это означает что дальше есть еще данные. В последней строчки этого знака быть не должно. CertificateTemplate = TermFarm; -- это имя шаблона, можно задать во время регистрации -- - этот значок обозначает начало коментария. Его не должно быть в файле.

  2. После того, как мы создали файл inf заходим на выдающий сервер

  3. Запускаем из-под админа cmd

  4. Запускаем certreq -new C:\%сert_patch%\%cert_name%.inf (исходный файл inf) C:\%сert_patch%\%cert_name%.req (итоговый файл req)

  5. Запускаем CERTREQ -attrib «CertificateTemplate:WebServer» C:\%сert_patch%\%cert_name%.req

    attrib - это имя шаблона, по которому делается сертификат. Он определяет, что именно можно удостоверять этим сертификатом. Стандартно это проверка подлинности сервера, бывают еще проверка подлинности клиента, подпись кода, почта, еще много всякого разного C:\%сert_patch%\%cert_name%.csr - это путь к файлу запроса. Может быть csr (я так понимаю в основном *nix-системы) или req (я так понимаю windows)

  6. Вылезет окошко с выбором удостоверяющего центра:
    image

  7. Если нам повезло и мы нигде не ошиблись у нас появится окно с предложением сохранить файл с расширением .cer – это как раз то, что нам нужно – подписанная открытая часть.
    Казалось бы, вот оно счастье. Ан нет, нужно еще склеить полученное с закрытой частью. Для этого запускаем mmc из под админа

  8. Добавляем оснастку «Сертификаты»

  9. Выбираем «учетная запись компьютера» (это важно, иначе не увидеть закрытую часть) и в итоге получаем вот такое:
    image

  10. Сертификаты -> Запросы заявок на сертификат ->Сертификат, правой клавишей мышки -> Все задачи -> импорт
    (если посмотреть в список сертификатов, то мы должны увидеть наш сертификат (называться будет так же, как строка Subject в файле inf)

  11. Далее -> Тут выбираем полученный cer -> Далее -> Готово

  12. Выбираем наш сертификат -> правая клавиша мыши -> все задачи -> экспорт

  13. В появившемся окне Далее -> Да, экспортировать закрытый ключ (далее) -> Ставим галки «включить все сертификаты» и «экспортировать все расширенные свойства» (Далее) -> ставим галку «Пароль» и вбиваем невероятно сложный пароль 1 (на самом деле если куда-то далеко и кому то, то пароль действительно должен быть сложный) Далее -> выбираем куда сохранять -> Готово

  14. Забираем сертификат и скармливаем ее программе/железке
    Нюанс. Некоторые программы требую формат p12 или как то так, но вполне шикарно принимают и pfx

    Установка pfx на Nginx

    • Копируем pfx на машину с Nginx
    • Получаем из pfx сертификат
      openssl pkcs12 -in mydomain.pfx -clcerts -nokeys -out mydomain.com.cer
    • Получаем из pfx закрытый ключ
      openssl pkcs12 -in domain.pfx -nocerts -nodes -out mydomain.com.key


ссылка на оригинал статьи https://habr.com/post/424743/

Распознавание жестов с помощью APDS-9960

image

Читая комментарии к моей предыдущей статье про APDS-9960, где речь шла про распознавание цвета и уровня освещенности для меня стали очевидными две вещи: 1) тема распознавания жестов интересна и 2) тема эта не раскрыта.

Действительно, если уж взялся за описание APDS-9960, то без рассмотрения жестов описание это выглядит несколько незавершенным. Поэтому я нашел свободное время, чтобы исследовать и эту тему тоже.

В данной статье я предлагаю Вашему вниманию обзор возможностей для распознавания жестов которые предоставляет сенсор APDS-9960.

В статье будет рассмотрен механизм настройки сенсора, сбор данных, их обработка и представление. Вы сами сможете убедиться в том насколько это просто — работать с жестами с помощью APDS-9960.
Как и в прошлый раз, статья будет сопровождаться кодом, все происходящее в котором будет подробно описано. Полная версия кода доступна в конце статьи.

Сразу небольшая ремарка: встроенного автоматического механизма определения жестов у APDS-9960 не предусмотрено, то есть такого, чтобы прям вот, прочитал, значит, регистр, а там уже и жест обработанный лежит — такого в APDS-9960 нет; а это означает, что придется писать свой алгоритм интерпретации жестов, чем впоследствии и займемся.

Вообще, это одновременно и хорошо и не очень. Не очень — потому что может усложнить исследование данного сенсора для начинающего, а хорошо, потому что, вкупе с данными о приближении, можно, изощряясь, вообще напридумывать собственных жестов различных каких угодно разнообразных и всяких.

Но, поскольку данная статья несет лишь обзорную функцию, мы ограничимся только базовыми UP-DOWN-LEFT-RIGHT жестами.

Ну что же, приступим.

Теория

Позволю себе чуточку матчасти.

Для получения необходимой информации о движении и направлении движения в APDS-9960 используются ИК светодиод и четыре фотодиода, которые, как наглядно проиллюстрировано на рисунке ниже, регистрируют сигналы в диапазоне ближнего ИК (NIR).

image

ИК светодиод (LED) несет функцию подсветки, а фотодиоды (UDLR) регистрируют отраженный от «препятствия» свет.

Фотодиоды расположены на сенсоре таким образом, что в зависимости от направления движения «препятствия», соответствующий фотодиод получит большую часть отраженного ИК-сигнала на входе и меньшую часть на выходе. В то же время документация на APDS-9960 недвусмысленно подсказывает нам, что интерпретировать направление движения можно измеряя и сравнивая амплитуду и разность фаз сигналов с фотодиодов UDLR.

image

Практика

Для работы с APDS-9960, как и в прошлой раз, будем использовать STM32VLDISCOVERY. Подключение также не поменялось.

Настройка APDS-9960

Производим первоначальную настройку сенсора.

Вот так вот:

APDS9960_init

void APDS9960_init(void) {  	i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 	i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); 	i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); 	i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 	i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); 	i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH);  } 

Что же здесь происходит? Давайте разбираться.

i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN);

PGAIN (Proximity Gain Control) — это параметр который управляет коэффициентом усиления чувствительности приближения. Присвоим ему значение 2, что соответствует усилению в четыре раза.

i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); 

GPENTH (Gesture Proximity Enter Threshold Register) — этот параметр устанавливает пороговое значение близости для определения начала распознавания жеста.
GEXTH (Gesture Exit Threshold Register), соответственно, устанавливает пороговое значение для определения окончания распознавания жеста.

i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

В регистре GCONF2 (Gesture configuration two) мы явно устанавливаем только параметр GGAIN (Gesture Gain Control) в значение усиления в четыре раза.

i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); 

Подсветка. По умолчанию значение для источника тока ИК светодиода подсветки установлен в значение 0, что соответствует току в 100 мА, нас это вполне устроит — менять не будем.
ИК подсветка в APDS-9960 представляет собой последовательность импульсов и характеризуется соответствующими параметрами регистров для жестов GPULSE (Gesture pulse count and length): GPLEN (Gesture Pulse Length) и GPULSE (Number of Gesture Pulses), а также приближения PPULSE (Proximity Pulse Count Register): PPLEN (Proximity Pulse Length) и PPULSE (Proximity Pulse Count) задающими количество импульсов и период каждого отдельного импульса.
Определим, что GPLEN и PPLEN примут значение 2 равное 16 мкс, а GPULSE и PPULSE значение 9, которое соответствует 10 импульсам.

Как видите, настройка оказалась не немного сложнее аналогичной для распознавания цветов и освещения из предыдущего обзора APDS-9960.

Чтение данных

Теперь переместимся в основной цикл программы, в котором начнем то и дело регистрировать и интерпретировать данные с фотодиодов, а также научимся находить отличия одного жеста от другого.

Перво-наперво, стартуем APDS-9960 с функциями работы с жестами и приближением.

GesturesSet(GESTURES_START); 

И сразу же начинаем отслеживать параметр GVALID. GVALID (Gesture FIFO Data) — это параметр регистра GSTATUS (Gesture Status Register), который, находясь в отличном от нуля состоянии, сообщает нам о том, что у сенсора имеются пригодные для использования данные о жестах.

Документация учит нас, что информация о жестах находится в буфере, в области оперативной памяти, которая в общем случае имеет размер 32 x 4 байт.
На практике, фактический размер этого буфера можно узнать прочитав значение регистра GFLVL (Gesture FIFO level), т.е. по моим сугубо эмпирическим экспериментальным наблюдениям, получается GFLVL*4. Как-то так:

image

Ну и как следует из названия буфера, данные в нем располагаются в порядке First In — First Out. То есть, грубо говоря, чем «раньше» поступил сигнал с каждого из фотодиодов тем «выше» в GFLVL он располагается.

Данные с фотодиодов (UDLR) можно прочитать из соответствующих регистров Gesture FIFO Register:

— GFIFO_U (Gesture FIFO Data, UP)
— GFIFO_D (Gesture FIFO Data, DOWN)
— GFIFO_L (Gesture FIFO Data, LEFT)
— GFIFO_R (Gesture FIFO Data, RIGHT)

После каждого чтения значений из этих регистров, GFLVL декрементируется; таким образом, по хорошему, необходимо произвести чтение полностью всего буфера до момента пока GFLVL не достигнет нуля.

Для определения жестов нам понадобятся только первые четыре байта этого буфера, не больше. Поэтому и прочитаем мы только их.

GestureUp = i2c1_read(APDS9960_GFIFO_U); GestureDown = i2c1_read(APDS9960_GFIFO_D); GestureLeft = i2c1_read(APDS9960_GFIFO_L); GestureRight = i2c1_read(APDS9960_GFIFO_R); 

Распознавание жестов

Чтобы интерпретировать какой же именно жест произошел, произведем нехитрые вычисления:

GestUpDown = GestureUp-GestureDown; GestLeftRight = GestureLeft-GestureRight; 

Для регистрации жестов нам важны не сами значения GestUpDown и GestLeftRight, а только лишь знак, так сказать, вещественного числа.
То есть, иными словами, принимая на вход отрицательные и положительные значения переменных GestUpDown и GestLeftRight определяем какой именно жест совершен.

Таблица истинности для переменных GestUpDown и GestLeftRight представлена на рисунке ниже

image

Теперь обнулим GFLVL:

GesturesSet(GESTURES_STOP); 

… и вернемся в начало основного цикла программы.

А теперь весь код целиком:

main.c

 #include "stm32f10x.h"  #define APDS9960_I2C_ADDR       0x39 #define APDS9960_ENABLE         0x80 #define APDS9960_GSTATUS        0xAF #define APDS9960_GFLVL          0xAE  //Gesture FIFO Register (0xFC – 0xFF): #define APDS9960_GFIFO_U        0xFC #define APDS9960_GFIFO_D        0xFD #define APDS9960_GFIFO_L        0xFE #define APDS9960_GFIFO_R        0xFF   #define APDS9960_CONTROL        0x8F #define APDS9960_GPENTH         0xA0 #define APDS9960_GEXTH          0xA1 #define APDS9960_GCONF2         0xA3 #define APDS9960_GPULSE         0xA6 #define APDS9960_PPULSE         0x8E  #define GESTURES_START          0x01 #define GESTURES_STOP           0x02  #define DEFAULT_GPENTH          40      // Threshold for entering gesture mode #define DEFAULT_GEXTH           30      // Threshold for exiting gesture mode     #define DEFAULT_PGAIN           8 			// Proximity Gain Control: 4X  #define DEFAULT_GGAIN           0x40		// Gesture Gain Control: 4X  #define DEFAULT_PULSE_LENGTH    0x89    // 16us, 10 pulses  /* Bit fields */ #define APDS9960_PON            0x01 #define APDS9960_AEN            0x02 #define APDS9960_PEN            0x04 #define APDS9960_WEN            0x08 #define APSD9960_AIEN           0x10 #define APDS9960_PIEN           0x20 #define APDS9960_GEN            0x40 #define APDS9960_GVALID         0x01  int GestUpDown = 0; int GestLeftRight = 0;  //-----------------------------------------------------------------------  uint8_t i2c1_read(uint8_t addr); void i2c1_write(uint8_t addr, uint8_t data);  void I2C1_init(void) { 	 	I2C_InitTypeDef I2C_InitStructure;   GPIO_InitTypeDef  GPIO_InitStructure;    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO , ENABLE);  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; 	GPIO_Init(GPIOB, &GPIO_InitStructure); 	 	I2C_StructInit(&I2C_InitStructure); 	I2C_InitStructure.I2C_ClockSpeed = 100000; 	I2C_InitStructure.I2C_OwnAddress1 = 0x01; 	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; 	I2C_Init(I2C1, &I2C_InitStructure); 	I2C_Cmd(I2C1, ENABLE);  }  //-----------------------------------------------------------------------  void APDS9960_init(void) {  	i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN);		 	i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); 	i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); 	i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 	i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); 	i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); 	 }  //-----------------------------------------------------------------------  uint8_t i2c1_read(uint8_t addr) { 	uint8_t data; 	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); 	I2C_GenerateSTART(I2C1, ENABLE); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 	I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); 	I2C_SendData(I2C1, addr); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); 	I2C_GenerateSTART(I2C1, ENABLE); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 	I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Receiver); 	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); 	data = I2C_ReceiveData(I2C1); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); 	I2C_AcknowledgeConfig(I2C1, DISABLE); 	I2C_GenerateSTOP(I2C1, ENABLE); 	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); 	return data; }  //-----------------------------------------------------------------------  void i2c1_write(uint8_t addr, uint8_t data) { 	I2C_GenerateSTART(I2C1, ENABLE); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 	I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); 	I2C_SendData(I2C1, addr); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); 	I2C_SendData(I2C1, data); 	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); 	I2C_GenerateSTOP(I2C1, ENABLE); 	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {};  }  //-----------------------------------------------------------------------  void GesturesSet(uint8_t GestSel) { 	 	switch (GestSel)   	{   		case GESTURES_START: 			i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON); 			break; 		case GESTURES_STOP: 			i2c1_write(APDS9960_ENABLE, APDS9960_PEN | APDS9960_PON); 			break;   		default: 			i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON);			 	} 	 }  //-----------------------------------------------------------------------  int main() { 	 	uint8_t GFLVL_buf = 0; 	 	uint8_t GSTATUS_buf = 0;  	uint8_t GestureUp = 0; 	uint8_t GestureDown = 0; 	uint8_t GestureLeft = 0; 	uint8_t GestureRight = 0; 	 	I2C1_init(); 	 	APDS9960_init();    while (1)   {  		GFLVL_buf = 0; 		 		GSTATUS_buf = 0;  		GestureUp = 0; 		GestureDown = 0; 		GestureLeft = 0; 		GestureRight = 0; 		 		GestUpDown = 0; 		GestLeftRight = 0; 		 		GesturesSet(GESTURES_START);  		GSTATUS_buf = i2c1_read(APDS9960_GSTATUS);  		if(GSTATUS_buf & APDS9960_GVALID) { 		 			GFLVL_buf = i2c1_read(APDS9960_GFLVL);  			if(GFLVL_buf) { 				 				GestureUp = i2c1_read(APDS9960_GFIFO_U); 				GestureDown = i2c1_read(APDS9960_GFIFO_D); 				GestureLeft = i2c1_read(APDS9960_GFIFO_L); 				GestureRight = i2c1_read(APDS9960_GFIFO_R);				  				//Truth table: 				//UP: 	 GestUpDown(+) | GestLeftRight(+) 				//DOWN:  GestUpDown(-) | GestLeftRight(-) 				//LEFT:  GestUpDown(+) | GestLeftRight(-) 				//RIGHT: GestUpDown(-) | GestLeftRight(+) 				 				GestUpDown = GestureUp-GestureDown; 				GestLeftRight = GestureLeft-GestureRight; 						 				GesturesSet(GESTURES_STOP);  			} 			 		} 			 	}  }    

Хочу отметить, что механизм жестов у APDS-9960 работает очень даже неплохо. Распознавание стабильное, хорошо работают встроенные в APDS-9960 UV and IR фильтры.

Надеюсь, данный материал кому-нибудь окажется полезен. Спасибо за внимание.


ссылка на оригинал статьи https://habr.com/post/424947/

Смартфон управляет игрушечным автомобилем

За прошедшее десятилетие у многих появилось один два смартфона лежащих без дела, так и у меня. Решил применить смартфон для управления чем-нибудь, ну например, автомобилем.
Для начала выбрал игрушечный автомобиль из Лего. Он может двигаться вперед и назад, поворачивать вправо и влево и имеет пульт ДУ c ИК лучами. В качестве драйвера двигателей машинки используется микросхема LB1836, которая имеет четыре информационных входа. In1, In2 — управляют ходовым двигателем, In3, In4 — управляют приводом руля, по схеме 0, 0 или 1, 1 — нерабочее состояние. 0, 1 или 1, 0 — вращение двигателя в одну или другую сторону.

Логические 0 и 1 на входах формирует приемник ИК лучей игрушки при получении сигналов от ДУ.

Я для передачи сигналов от смартфона к машинке решил использовать экран смартфона, поскольку другие каналы передачи технически сложные и дорогие в реализации. К входам микросхемы LB1836 я решил подсоединить свое устройство, которое будет получать сигналы от смартфона.

Схема передачи сигналов такая. Программа в смартфоне создает световые пятна заданной продолжительности на экране, устройство их регистрирует и формирует логические 0 и 1 той же продолжительности на входах LB1836 без какой либо логической обработки, что очень упрощает конструкцию устройства. То есть, я просто подменил приемник ИК лучей своим устройством.

Устройство приема световых сигналов состоит из 4 ячеек. Каждая ячейка представляет собой фототранзистор L-3DP3C KGB подсоединенный к усилителю на npn транзисторе KT315. Питание берется с управляемой машинки. Выход, как я уже говорил, подсоединяется к соответствующему входу драйвера двигателя. Маломощные двигатели могут питаться прямо от выхода, но вращаться будут только в одну сторону. Но зато их будет четыре!

О выборе элементов устройства могу сказать следующее. Транзисторы я взял те, какие у меня были с давних времен. Фототранзисторы купил те, что были в магазине. Так что, желающие повторить схему, не тратьте время на поиски, воспользуйтесь теми элементами, что вам доступны. Кстати, вначале я усилитель собрал на микросхеме LM 324N с четырьмя операционными усилителями. Тоже хорошо работает, но мне показались его габариты слишком большими.
Устройство собрал на картонке, используя полоски медной фольги и элементы конструктора Лего.

Фототранзисторы помещены на дно световых колодцев диаметром 5мм и глубиной 9 мм, что бы исключить боковое засвечивание. В световые колодцы вставлены металлизированные плёнки, свёрнутые в трубочку, что значительно повышает чувствительность фототранзисторов, за счет увеличения светового потока.

Вот такой получился автомобиль.

Для разработки программы я взял, приложение BASIC! из Googl Play Маркет. Это приложение позволяет писать программы для Android, создавать файлы apk и работать со всеми сенсорами и устройствами смартфона.

Листинг программы

DO  LET s$="Говорите команду: вперёд назад вправо влево или стоп"  TTS.INIT  TTS.SPEAK s$  TTS.STOP   STT.LISTEN "Говорите"  STT.RESULTS theList  LIST.SIZE theList, theSize  LET kom$="неудача"   FOR k = 1 TO theSize   LIST.GET theList, k, theText$   IF (theText$="вперед") THEN kom$="вперёд"   IF (theText$="назад") THEN kom$="назад"   IF (theText$="вправо") THEN kom$="вправо"   IF (theText$="влево") THEN kom$="влево"   IF (theText$="стоп") THEN kom$="стоп"  NEXT k  PRINT kom$    GR.OPEN 255, 0, 0, 0  GR.BRIGHTNESS 0.7  GR.ORIENTATION 1  GR.SCREEN w, h   a = 255  r = 255  g = 255  b = 255  fill = 1  GR.COLOR a,r,g,b,fill   LET wc=0.95*h  LET hc=w  LET m=36  GR.CLS   GR.CIRCLE n5, hc/m*9.5,wc,w/15.6  GR.CIRCLE n4, hc/m*13.5,wc,w/15.6  GR.CIRCLE n3, hc/m*18,wc,w/15.6  GR.CIRCLE n2, hc/m*22.5,wc,w/15.6   GR.CIRCLE n1, hc/m*26.5,wc,w/15.6   FOR i=1 TO 5   GR.HIDE i  NEXT i   IF (kom$="вперёд") THEN    GR.SHOW 2   PAUSE 300   GR.HIDE  2   ENDIF   IF (kom$="назад") THEN   GR.SHOW 1   PAUSE 300   GR.HIDE 1  ENDIF   IF (kom$="влево") THEN   GR.SHOW 2   GR.SHOW 4   PAUSE 300   GR.HIDE 2   GR.HIDE 4  ENDIF   IF (kom$="вправо") THEN   GR.SHOW 2   GR.SHOW 5   PAUSE 300   GR.HIDE 2   GR.HIDE 5  ENDIF   IF (kom$="стоп") THEN GOTO  qq  GR.BRIGHTNESS  0.1  GR.CLOSE UNTIL 0      ! ONERROR:  qq: LET s$="программа закончила работу" TTS.INIT TTS.SPEAK s$ TTS.STOP GR.BRIGHTNESS 0.1 GR.CLOSE END 

В программе используется управление голосом как наиболее целесообразное в моем случае. Объект TTS преобразует текст в речь. Объект STT преобразует речь в текст. При работе STT обязательно должен быть включен Wi Fi, потому что записанная фонограмма отправляется на сервер в интернете, откуда приходит результат распознавания, это несколько слов, или фраз. Программа проверяет, есть ли команда в списке, выводит команду на экран и выполняет её. Если текст не распознан программа выводит сообщение об этом. Все это повторяется в бесконечном цикле. Завершение работы программы производится командой «стоп» или нажатием клавиши возврат. Геометрические размеры взяты относительными, чтобы автоматизировать привязку на разных экранах с разными размерами и разрешением, что не всегда получается. Круги, которые рисуются на экране, являются объектами и номеруются автоматически по мере их создания.

Команда GR.SHOW 2 — означает показать круг, который был создан вторым. Его номер содержится в n4. Можно было написать GR.SHOW n4. В строке IF (theText$=«вперед») THEN kom$=«вперёд» вы заметите различие в значениях величины в команде присваивания — «вперед» и «вперёд». Дело в том, что STT упорно возвращает «вперед», хотя бывают случаи «вперёд», тогда программа не распознает команду. От этого можно было избавиться, но я не стал усложнять программу, а просто поправил написание при выводе на экран. Еще бывают сбои при распознавании команды «влево». Впрочем, возможно это особенности моего произношения.

К сожалению, я не могу вставить сюда клип показывающий работу программы.

Желающие могут посмотреть перейдя по ссылке.


ссылка на оригинал статьи https://habr.com/post/424945/

Как я взломал систему отслеживания ошибок Google и получил вознаграждение в $15,600

Вы когда-нибудь слышали о Google Issue Tracker? Вероятно нет, если только вы не являетесь сотрудником Google или разработчиком, который недавно сообщил об ошибках в продуктах Google. Я тоже не слышал, пока не заметил, что мои отчеты об уязвимостях теперь обрабатываются путем открытия новой темы в дополнение к обычным уведомлениям по электронной почте.

Я немедленно попытался сломать его.



Что же это за сайт? Согласно документации, Issue Tracker (между собой его называют Buganizer System) — это служебный инструмент, используемый компанией Google для отслеживания ошибок и запросов во время разработки продукта. Он доступен и вне Google для использования внешними партнерами, которые сотрудничают с Google по конкретным проектам.

Другими словами, когда у кого-то возникают проблемы при использовании продуктов Google, они идут в баг-трекер. Разумно, не так ли? Мы, как внешние пользователи, видим только верхушку айсберга: небольшой набор заранее согласованных категорий и проблемы, когда кто-то из Google добавит внешний аккаунт, например, отчеты об уязвимостях. Но сколько информации скрыто под покровом?

Наблюдая за числовыми ID последних публичных тем, мы можем легко оценить как часто используется этот инструмент внутри. Около 2000-3000 обращений в час и только 0,1% из них являются публичными. Похоже, утечка данных в этой системе будет иметь большое значение. Надо взламывать!

Попытка №1. Получение рабочего аккаунта Google

Первое, что я заметил, наблюдая за системой отслеживания проблем — это возможность участвовать в обсуждениях, отправляя письма на специальный адрес, который выглядит следующим образом:

buganizer-system+componentID+issueID@google.com

(в котором componentID – это номер определенной категории, а issueID — уникальный идентификатор для темы, на которую вы отвечаете)

Это напомнило мне недавнюю находку под названием Ticket Trick, которая позволяла взломщикам проникать в чат организаций, используя систему электронной почты. Учитывая, что это адрес на google.com, я пытался зарегистрироваться в Slack команды Google, и страница с подтверждением, которую я получил, выглядела очень многообещающе:

Увы, ни одного письма от Slack так и не появилось.

Следующее, что я смог придумать — это получить Google аккаунт с основным адресом электронной почты на домене google.com, который предоставил бы мне дополнительные привилегии в Buganizer. Регистрация такого аккаунта вне Google не допускалась:

Однако я нашел метод обхода этого фильтра: если зарегистрироваться с любого другого поддельного адреса, но не подтверждать аккаунт через ссылку, полученную по электронной почте, то появляется возможность изменять свой адрес электронной почты без каких-либо ограничений. Используя этот метод, я изменил адрес нового аккаунта Google на следующий: buganizer-system+123123+67111111@google.com.

Вскоре после этого я получил письмо с подтверждением в виде сообщения:

Прекрасно! Я нажал на ссылку подтверждения, вошел в Issue Tracker и…

Меня перенаправили на страницу для корпоративного входа. И нет, мои учетные данные Google-аккаунта там не работали. Облом.

Тем не менее, этот аккаунт дал мне много преимуществ в Интернете, включая возможность корпоративной развозки, поэтому это все еще было проблемой, открывавшей много возможностей для злоумышленников.

Затрачено: 11 часов | Вознаграждение: $3,133.7 | Приоритет: Р1

Попытка №2. Получение уведомлений о внутренних тикетах

Еще одна функция Issue Tracker, которая привлекла мое внимание во время знакомства с пользовательским интерфейсом — это добавление в избранное. Отметка звездочкой означает, что вас интересует обсуждаемая проблема, и вы хотите получать уведомления по электронной почте, когда кто-то добавляет комментарий.

Интересная вещь, которую я заметил применительно к этой функции заключалась в том, что наблюдалось хорошо заметное отсутствие ошибок при попытке использовать ее для тех тем, к которым у меня не было доступа. Видимо, правила контроля доступа никогда не применялись к этому показателю, поэтому я вошел в свой второй аккаунт и попытался отметить отчет об уязвимости из моего главного аккаунта, заменив в запросе Issue ID. После этого я увидел это сообщение, которое означало, что действие было успешным:

1 person has starred this issue.

Означало ли это, что можно легко отслеживать открытые уязвимости Google? Я быстро опубликовал комментарий по этому вопросу, чтобы увидеть – придет ли уведомление на мой фиктивный аккаунт?

Но вновь, не появилось никаких писем.

По какой-то причине, которую я уже и не помню, я решил провести еще один тест. Так как у меня были номера последних ID с запросами, то я экстраполировал несколько тысяч ID, которые должны были совпадать с последними обращениями в базе данных. Я отметил их все.

Через несколько минут мой почтовый ящик выглядел так:

Когда я открыл почтовый ящик, моя первая мысль была – «джекпот!».

Но при ближайшем рассмотрении в этих сообщениях не было ничего особо интересного. По-видимому, я мог только подслушивать обсуждения, связанные с переводом, во время которых люди спорили по поводу наилучшего способа передать значение той или иной фразы на разных языках.

Я даже подумывал не сообщать об этом, надеясь найти способ усилить степень опасности. В конце концов я понял, что команда безопасности Google будет заинтересована в поиске возможных методов и вариантов, поэтому я отправил им все детали.

Затрачено: 5 часов | Вознаграждение: $5,000 | Приоритет: Р0

Попытка №3. Игра окончена

Когда вы посещаете баг-трекер в качестве внешнего пользователя, многие его функции недоступны, что существенно ограничивает ваши возможности. Если вы хотите увидеть все те классные вещи, которые доступны сотрудникам Google, то вы можете поискать конечные точки API в файлах JavaScript. Некоторые из этих функций полностью отключены, другие же просто скрыты в интерфейсе.

Когда разрабатывали эту ограниченную версию системы, кто-то был достаточно мил, чтобы отойти от метода, в соответствии с которым нас удаляют из списка вторичных адресатов (CC), в случае, если мы потеряли интерес к проблеме или не хотим получать электронные письма о ней. Этого можно добиться, отправив запрос следующим образом:

POST /action/issues/bulk_edit HTTP/1.1

{
"issueIds":[
67111111,
67111112
],
"actions":[
{
"fieldName":"ccs",
"value":"test@example.com",
"actionType":"REMOVE"
}
]
}

Однако я заметил некоторые упущения, которые привели к огромной проблеме:

  1. Контроль несанкционированного доступа: нет четкой проверки, что текущий пользователь действительно имеет доступ к обращениям, указанным в issueIds, перед попыткой выполнить заданное действие;
  2. Безмолвный отказ: если вы указали адрес электронной почты, который не был включен в список CC, то конечная точка вернет сообщение о том, что письмо было успешно удалено;
  3. Детальная информация об обращении в ответе: если не было обнаружено ошибок во время совершения действия, то другая часть системы предполагает наличие у пользователя соответствующего разрешения. Поэтому каждая деталь для заданного issueID будет возвращена в теле HTTP ответа.

Теперь я могу просмотреть подробности о каждом обращении в базе данных, заменив issueIds в запросе. Бинго!

Я только попробовал просмотреть несколько последовательных ID, а затем атаковал самого себя со стороннего аккаунта, чтобы подтвердить серьезность этой проблемы.

Да, я смог увидеть подробности в отчетах об уязвимостях, как и много другое, размещенное в Buganizer.

Хуже того, я мог бы извлечь данные о нескольких тикетах одним запросом, значит мониторинг всей внутренней активности в реальном времени не привел бы к наложению каких-то ограничений.

Я быстро отправил информацию об уязвимости в Google, и их команда безопасности отключила поврежденную конечную точку в течение часа. Впечатляющее время реакции!

Затрачено: 1 час | Вознаграждение: $7,500 | Приоритет: Р0

Когда я впервые начал искать утечку информации, я предположил, что настоящей чашей Грааля будут баги в системе Google, потому что они позволяют найти информацию обо всех других багах (например, HackerOne платит минимум 10 000 долларов за нечто похожее).

Но обнаружив их, я быстро понял, что воздействие будет минимизировано, так как критические уязвимости в любом случае будут нейтрализованы в течение часа.

Я очень рад, что получил дополнительные средства, и с нетерпением стремлюсь к нахождению ошибок в других продуктах Google.


Заглядывайте на VPS.today — сайт для поиска виртуальных серверов. 1500 тарифов от 130 хостеров, удобный интерфейс и большое число критериев для поиска самого лучшего виртуального сервера.


ссылка на оригинал статьи https://habr.com/post/424811/

Потоковая передача данных из REST сервиса в MQ очередь

Привет, Хабр!

В статье я опишу способ разработки REST сервиса, позволяющего принимать файлы и сохранять их в систему обмена сообщениями в потоковом режиме без необходимости хранения всего файла на стороне сервиса. Также будет описан обратный сценарий, при котором клиент будет получать в качестве ответа файл, размещенный в системе обмена сообщениями.

Для наглядности я приведу примеры кода разработанного сервиса на JEE7 под сервер приложений IBM WebSphere Liberty Server, а в качестве системы обмена сообщениями будет выступать IBM MQ.
Тем не менее, описанный метод подходит и для других аналогичных платформ, т.е. в качестве системы обмена сообщений может выступать любой поставщик JMS API, а в качестве сервера приложений любой JEE сервер (например, Apache Tomcat).

Постановка задачи

Возникла потребность в реализации решения, которое бы позволяло как получать от клиента файлы большого размера (> 100 Mb) и передавать их в другую территориально удаленную систему, так и в обратную сторону – передавать клиенту в качестве ответа файлы из этой системы. В виду ненадежного сетевого канала между сетью клиента и сетью приложения используется система обмена сообщениями, обеспечивающая гарантированную доставку между ними.

Верхнеуровневое решение включает в себя три компонента:

  1. REST сервис – задача которого предоставить клиенту возможность передать файл (или запросить).
  2. MQ – отвечает за передачу сообщений между различными сетями.
  3. Application – приложение, отвечающее за хранение файлов и выдачу их по запросу.

image

В этой статье я описываю способ реализации REST сервиса, в задачи которого входит:

  • Получение файла от клиента.
  • Передача полученного файла в MQ.
  • Передача файла из MQ клиенту в качестве ответа.

Метод решения

В виду большого размера передаваемого файла отсутствует возможность размещения его полностью в оперативной памяти, более того, со стороны MQ также накладывается ограничение – максимальный размер одного сообщения в MQ не может превышать 100 Mb. Таким образом мое решение будет основываться на следующих принципах:

  • Получение файла и сохранение его в MQ очереди должно выполняться в потоковом режиме, без помещения в память полностью файла.
  • В очереди MQ файл будет размещаться в виде набора небольших сообщений.

Графически размещение файла на стороне клиента, REST сервиса и MQ показано ниже:

image

На стороне клиента файл полностью размещается на файловой системе, в REST-сервисе в оперативной памяти хранится лишь порция файла, а на стороне MQ – каждая порция файла размещается в виде отдельного сообщения.

Разработка REST сервиса

Для наглядности предлагаемого метода решения будет разработан демонстрационный REST сервис, содержащий два метода:

  • upload – получает от клиента файл и записывает его в MQ очередь, в качестве ответа возвращает идентификатор группы сообщений (в base64 формате).
  • download – получает от клиента идентификатор группы сообщений (в base64 формате) и возвращает файл, хранящийся в MQ очереди.

Метод получения файла от клиента (upload)

В задачу метода входит получение потока входящего файла и последующая запись его в MQ очередь.

Получение потока входящего файла

Для получения входящего файла от клиента, метод ожидает в качестве входящего параметра объект с интерфейсом com.ibm.websphere.jaxrs20.multipart.IMultipartBody, который предоставляет возможность получить ссылку на поток входящего файла

@PUT @Path("upload") public Response upload(IMultipartBody body) { 	... 	IAttachment attachment = body.getAttachment("file"); 	InputStream inputStream = attachment.getDataHandler().getInputStream(); 	... }

Данный интерфейс (IMultipartBody) находится в JAR-архиве com.ibm.websphere.appserver.api.jaxrs20_1.0.21.jar, входит в поставку к IBM Liberty Server и размещается в папке: <WLP_INSTALLATION_PATH>/dev/api/ibm.

Примечание:

  • WLP_INSTALLATION_PATH — путь к директории WebSphere Liberty Profile.
  • Ожидается, что клиент будет передавать файл в параметре с именем «file».
  • Если используется другой сервер приложений, то можно воспользоваться альтернативной библиотекой от Apache CXF.

Потоковое сохранение файла в MQ

Метод получает на вход поток входящего файла, название MQ очереди, куда следует записать файл, и идентификатор группы сообщений, который будут использоваться для связывания сообщений. Идентификатор группы генерируется на стороне сервиса, например, утилитой org.apache.commons.lang3.RandomStringUtils:

String groupId = RandomStringUtils.randomAscii(24);

Алгоритм сохранения входящего файла в MQ состоит из следующих этапов:

  1. Инициализация объектов подключения к MQ.
  2. Цикличное считывание порции входящего файла пока файл не будет полностью считан:
    1. Порция данных файла записывается в виде отдельного сообщения в MQ.
    2. Каждое сообщение файла имеет свой порядковый номер (свойство «JMSXGroupSeq»).
    3. Все сообщения файла имеет одинаковое значение группы (свойство «JMSXGroupID»).
    4. Последнее сообщение имеет признак, означающий, что это сообщение является завершающим (свойство «JMS_IBM_Last_Msg_In_Group»).
    5. Константа SEGMENT_SIZE содержит размер порции. Например, 1Mb.

public void write(InputStream inputStream, String queueName, String groupId) throws IOException, JMSException { 	try ( 		Connection connection = connectionFactory.createConnection(); 		Session session = connection.createSession(); 		MessageProducer producer = session.createProducer(session.createQueue(queueName)); 	) { 		byte[] buffer = new byte[SEGMENT_SIZE]; 		BytesMessage message = null; 		for(int readBytesSize = 1, sequenceNumber = 1; readBytesSize > 0; sequenceNumber++) { 			readBytesSize = inputStream.read(buffer); 			if (message != null) { 				if (readBytesSize < 1) { 					message.setBooleanProperty("JMS_IBM_Last_Msg_In_Group", true); 				}	producer.send(message); 			} 			if (readBytesSize > 0) { 				message = session.createBytesMessage(); 				message.setStringProperty("JMSXGroupID", groupId); 				message.setIntProperty("JMSXGroupSeq", sequenceNumber); 				if (readBytesSize == SEGMENT_SIZE) { 					message.writeBytes(buffer); 				} else { 					message.writeBytes(Arrays.copyOf(buffer, readBytesSize)); 				} 			} 		} 	} } 

Метод отправки файла клиенту (download)

Метод получает идентификатор группы сообщений в формате base64, по которому считывает сообщения из MQ очереди и отправляет в качестве ответа в потоковом режиме.

Получение идентификатора группы сообщений

В качестве входящего параметра метод получает идентификатор группы сообщений.

@PUT @Path("download") public Response download(@QueryParam("groupId") String groupId) { 	... }

Потоковая передача ответа клиенту

Для передачи клиенту файла, хранящемуся в виде набора отдельных сообщений в MQ, в потоковом режиме следует создать класс с интерфейсом javax.ws.rs.core.StreamingOutput:

public class MQStreamingOutput implements StreamingOutput {  	private String groupId; 	private String queueName; 	 	public MQStreamingOutput(String groupId, String queueName) { 		super(); 		this.groupId = groupId; 		this.queueName = queueName; 	}  	@Override 	public void write(OutputStream outputStream) throws IOException, WebApplicationException { 		try { 			MQWorker().read(outputStream, queueName, groupId); 		} catch(NamingException | JMSException e) { 			e.printStackTrace(); 			new IOException(e); 		} finally { 			outputStream.flush(); 			outputStream.close(); 		}  	} } 

В классе реализуем метод write, который получает на вход ссылку на исходящий поток, в который будут записываться сообщения из MQ. Я добавил в класс еще название очереди и идентификатор группы, сообщения которой будут считываться.

Объект этого класса будет передан в качестве параметра для создания ответа клиенту:

@GET @Path("download") public Response download(@QueryParam("groupId") String groupId) { 	ResponseBuilder responseBuilder = null; 	try { 		MQStreamingOutput streamingOutput = new MQStreamingOutput(new String(Utils.decodeBase64(groupId)), Utils.QUEUE_NAME); 		responseBuilder = Response.ok(streamingOutput);	 	} catch(Exception e) { 		e.printStackTrace(); 	responseBuilder.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()); 	} 	return responseBuilder.build(); }

Потоковое считывание файла из MQ

Алгоритм считывания сообщений из MQ в исходящий поток состоит из следующих этапов:

  1. Инициализация объектов подключения к MQ.
  2. Цикличное считывание сообщений из MQ пока не будет считано сообщение с признаком завершающего в группе (свойство «JMS_IBM_Last_Msg_In_Group»):
    1. Перед каждым считыванием сообщения из очереди устанавливается фильтр (messageSelector), в котором задается идентификатор группы сообщений и порядковый номер сообщения в группе.
    2. Содержимое считанного сообщения записывается в исходящий поток.

public void read(OutputStream outputStream, String queueName, String groupId) throws IOException, JMSException { 	try( 		Connection connection = connectionFactory.createConnection(); 		Session session = connection.createSession(); 	) { 		connection.start(); 		Queue queue = session.createQueue(queueName); 		int sequenceNumber = 1; 		for(boolean isMessageExist = true; isMessageExist == true; ) { 			String messageSelector = "JMSXGroupID='" + groupId.replaceAll("'", "''") + "' AND JMSXGroupSeq=" + sequenceNumber++; 			try( 				MessageConsumer consumer = session.createConsumer(queue, messageSelector); 					) { 				BytesMessage message = (BytesMessage) consumer.receiveNoWait(); 				if (message == null) { 					isMessageExist = false; 				} else { 					byte[] buffer = new byte[(int) message.getBodyLength()]; 					message.readBytes(buffer); 					outputStream.write(buffer); 					if (message.getBooleanProperty("JMS_IBM_Last_Msg_In_Group")) { 						isMessageExist = false; 					} 				} 			} 		} 	} }

Вызов REST сервиса

Для проверки работы сервиса я воспользуюсь инструментом curl.

Отправка файла

curl -X PUT -F file=@<путь_к_файлу> http://localhost:9080/Demo/rest/service/upload

В ответ будет получена base64 строка, содержащая идентификатор группы сообщений, которую мы укажем в следующем методе для получения файла.

Получение файла

curl -X GET http://localhost:9080/Demo/rest/service/download?groupId=<base64_строка_идентификатор_группы_сообщений> -o <путь_к_файлу_куда_запишется_ответ>

Заключение

В статье был рассмотрен подход к разработке REST сервиса, позволяющему в потоковом режиме как получать и сохранять большие данные в очередь системы обмена сообщениями, так и считывать их из очереди для возвращения в виде ответа. Такой способ позволяет сократить использование ресурсов, и тем самым увеличить пропускную способность решения.

Дополнительные материалы

Подробнее об интерфейсе IMultipartBody, используемый для получения входящего потока файла — ссылка.

Альтернативная библиотека для получения файлов в потоковом режиме в REST сервисах – Apache CXF.

Интерфейс StreamingOutput для потокового возвращения REST ответа клиенту — ссылка.


ссылка на оригинал статьи https://habr.com/post/424941/